/* Author: Rodolfo Torres Email: rodolfo.torres@outlook.com LinkedIn: https://www.linkedin.com/in/rodolfo-torres-p License: This code is licensed under GPL-3.0 The code is licensed under the GPL-3.0 license, which is a widely used open-source license, ensuring that any derivative work is also open source. It grants users the freedom to use, modify, and distribute the software, as well as any modifications or extensions made to it. However, any modified versions of the software must also be licensed under GPL-3.0. For more details, please refer to the full text of the GPL-3.0 license at https://www.gnu.org/licenses/gpl-3.0.html. */ /*/ Not change any values of the variables below, use the "json/config.json" file to make your settings. /*/ let data_index = ""; let prompts_name = ""; let prompts_expert = ""; let prompts_image = ""; let prompts_background_color = ""; let prompts_training = ""; let chat_font_size = ""; let API_URL = ""; let source = ""; let google_voice = ""; let google_voice_lang_code = ""; let microphone_speak_lang = ""; let chat_minlength = 0; let chat_maxlength = 0; let lang_index = 0; let scrollPosition = 0; let use_text_stream = false; let display_microphone_in_chat = false; let display_avatar_in_chat = false; let display_contacts_user_list = false; let display_copy_text_button_in_chat = false; let filter_badwords = true; let display_audio_button_answers = true; let chat_history = true; let hasBadWord = false; let chat = []; let pmt = []; let array_widgets = []; let array_chat = []; let lang = []; let = badWords = [] let array_messages = []; let array_voices = []; let filterBotWords = ["Robot:", "Bot:"]; //---- end configs----// //Modify the option below according to the prompt you want to use. let user_prompt_lang = "en"; let textModal; let fileModal; let uuid = ''; let chatId; let recognition; if (window.location.protocol === 'file:') { alert('This file is not runnable locally, an http server is required, please read the documentation.'); } //Loads the characters from the config.json file and appends them to the initial slider loadData("json/config.json", ["json/prompts-" + user_prompt_lang + ".json", "json/lang.json", "json/badwords.json"]); /** * Function to load data from the given URL and an array of URLs using Promise.all and map functions. * * @param {string} url - The URL to fetch the data from. * @param {Array} urls - An array of URLs to fetch additional data from. * @returns {Promise} - A Promise that resolves with the fetched data and updates the necessary variables. */ function loadData(url, urls) { // Fetch data from the given url and an array of urls using Promise.all and map functions return Promise.all([fetch(url).then(res => res.json()), ...urls.map(url => fetch(url).then(res => res.json()))]) .then(([out, OutC, OutL, OutB, OutT]) => { // Extract necessary data from the response lang = OutL; if (filter_badwords) { badWords = OutB.badwords.split(',') } lang_index = lang.use_lang_index; use_text_stream = out.use_text_stream; display_avatar_in_chat = out.display_avatar_in_chat; display_microphone_in_chat = out.display_microphone_in_chat; microphone_speak_lang = out.microphone_speak_lang; google_voice = out.google_voice; google_voice_lang_code = out.google_voice_lang_code; display_contacts_user_list = out.display_contacts_user_list; display_copy_text_button_in_chat = out.display_copy_text_button_in_chat; display_audio_button_answers = out.display_audio_button_answers; filter_badwords = out.filter_badwords; chat_history = out.chat_history; chat_font_size = out.chat_font_size; loadSpeechRecognition(); copy_text_in_chat = display_copy_text_button_in_chat ? `` : ''; var s = document.createElement('style'); s.innerHTML = '.message-text{font-size:' + chat_font_size + ' !important;}'; document.head.appendChild(s); if (!display_contacts_user_list) { $(".toggle_employees_list").hide(); $(".col-contacts-border").hide(); } if (display_microphone_in_chat) { $("#microphone-button").show() } // Populate array_widgets with character data and create HTML elements for each character card $("#load-character").html(""); $(".ai-contacts-scroll").html(""); for (var i = 0; i < OutC.length; i++) { array_widgets.push({ 'id': OutC[i]['id'], 'name': OutC[i]['name'], 'widget_name': OutC[i]['widget_name'], 'image': OutC[i]['image'], 'welcome_message': OutC[i]['welcome_message'], 'display_welcome_message': OutC[i]['display_welcome_message'], 'training': OutC[i]['training'], 'description': OutC[i]['description'], 'chat_minlength': OutC[i]['chat_minlength'], 'chat_maxlength': OutC[i]['chat_maxlength'], 'max_num_chats_api': OutC[i]['max_num_chats_api'] }) $("#load-character").append(`
or tags
if (/|<\/code>||<\/pre>/g.test(str)) {
// Returns the string without replacing the characters inside the tags
return str;
}
// Replaces special characters with their respective HTML codes
str = str.replace(/[&<>"'`{}()\[\]]/g, (match) => {
switch (match) {
case '<': return '<';
case '>': return '>';
case '{': return '{';
case '}': return '}';
case '(': return '(';
case ')': return ')';
case '[': return '[';
case ']': return ']';
default: return match;
}
});
// Remove the stream <span class="get-stream">
str = str.replace(/<span\s+class="get-stream">/g, "");
// Remove the closing tag
str = str.replace(/<\/span>/g, "");
// Replaces the ```code``` snippet with code
str = str.replace(/```(\w+)?([\s\S]*?)```/g, '$2
').replace(/(\d+\.\s)/g, "$1").replace(/(^[A-Za-z\s]+:)/gm, "$1");
return str;
};
/**
* Copies the text content to the clipboard.
* @param {HTMLElement} button - The button element that triggers the copy action.
*/
function copyText(button) {
const div = button.parentElement;
const code = div.querySelector('.chat-response');
const range = document.createRange();
range.selectNode(code);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
button.innerHTML = lang["translate"][lang_index].copy_text2;
}
/**
* Copies the content of the tag to the clipboard.
* @param {HTMLElement} button - The button element that triggers the copy action.
*/
function copyCode(button) {
const pre = button.parentElement;
const code = pre.querySelector('code');
const range = document.createRange();
range.selectNode(code);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
button.innerHTML = lang["translate"][lang_index].copy_code2;
}
/**
* Clears the chat history for the specified target. Displays a confirmation dialog before clearing.
* @param {string} target - The target for clearing the chat history. Can be "all" to clear all characters' chat history or "current" to clear the current character's chat history.
*/
function clearChat(target) {
// Display confirmation dialog using SweetAlert2 library
Swal.fire({
title: lang["translate"][lang_index].confirmation_delete_chat1,
text: lang["translate"][lang_index].confirmation_delete_chat2,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: lang["translate"][lang_index].confirmation_delete_chat3,
cancelButtonText: lang["translate"][lang_index].confirmation_delete_chat4
}).then((result) => {
// If user confirms deletion
if (result.isConfirmed) {
// If target is "all", clear chat history for all characters
if (target == "all") {
for (var i = 0; i < array_widgets.length; i++) {
array_widgets[i]['last_chat'] = [];
}
// Display success message using SweetAlert2
Swal.fire(
lang["translate"][lang_index].confirmation_delete_chat5,
lang["translate"][lang_index].confirmation_delete_chat_all,
'success'
)
} else {
// Otherwise, clear chat history for current character only
array_widgets[data_index]['last_chat'] = [];
// Display success message using SweetAlert2
Swal.fire(
lang["translate"][lang_index].confirmation_delete_chat5,
lang["translate"][lang_index].confirmation_delete_current_chat,
'success'
)
}
// Clear chat display
$("#overflow-chat").html("");
// Reset chat history and add initial message
array_chat = [];
array_chat.push({
"name": prompts_name,
"message": prompts_training,
"training": true,
"isImg": false,
"date": currentDate()
})
// Save updated character data to local storage
localStorage.setItem("text_talk_v1", JSON.stringify(array_widgets));
// If enabled, display welcome message for current character
if (displayWelcomeMessage) {
responseChat(array_widgets[data_index]['welcome_message']);
}
}
})
}
/**
* Loads the chat history for the current character from the local storage.
*/
function loadChat() {
if (chat_history) {
checkClearChatDisplay();
for (var i = 0; i < array_widgets[data_index]['last_chat'].length; i++) {
const currentChat = array_widgets[data_index]['last_chat'][i];
if (currentChat.name === "User") {
if (currentChat.isImg === true) {
const imageID = Date.now();
const imgURL = Array.isArray(currentChat.imgURL) ? currentChat.imgURL.map(url => url).join('') : '';
const imgHtml = Array.isArray(currentChat.imgURL) ? currentChat.imgURL.map(url => ``).join('') : '';
const chatHtml = `
`;
$("#overflow-chat").append(chatHtml);
array_chat.push({ "name": "User", "message": currentChat.message, "isImg": true, imgURL: currentChat.imgURL, "date": currentDate() });
} else {
const chatResponse = escapeHtml(currentChat.message)
const chatHtml = `
`;
$("#overflow-chat").append(chatHtml);
array_chat.push({ "name": "User", "message": currentChat.message, "isImg": false, "date": currentDate() });
}
} else {
avatar_in_chat = display_avatar_in_chat ? `` : '';
audio_in_chat = display_audio_button_answers ? `` : '';
if (!currentChat.training) {
const chatResponse = escapeHtml(currentChat.message)
const chatHtml = `
${avatar_in_chat}
`;
$("#overflow-chat").append(chatHtml);
}
array_chat.push({ "name": prompts_name, "message": currentChat.message, "training": currentChat.training, "date": currentDate() });
}
}
hljs.highlightAll();
setTimeout(function () {
scrollChatBottom();
}, 10);
} else {
if (displayWelcomeMessage) {
responseChat(welcome_message);
}
}
}
/**
* Checks the display for the "Clear Chat" option based on the chat history for the current character.
*/
function checkClearChatDisplay() {
if (array_widgets[data_index] && array_widgets[data_index].last_chat && array_widgets[data_index].last_chat.length > 1) {
if (chat_history) {
$("#clear-chat").show();
}
} else {
$("#clear-chat").hide();
}
// Check if there is chat history for any character
const hasLastChat = array_widgets.some((result) => {
return result.last_chat && result.last_chat.length > 2;
});
// Display or hide the "Clear All Chats" option based on the presence of chat history
if (hasLastChat) {
$("#clear-all-chats").show();
} else {
$("#clear-all-chats").hide();
}
}
/**
* Hides the error messages shown on the screen.
*/
function hideFeedback() {
toastr.remove()
}
/**
* Forces the chat to scroll to the bottom of the conversation.
*/
function scrollChatBottom() {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
let body = document.getElementsByTagName("html")[0];
body.scrollTop = body.scrollHeight;
} else {
let objDiv = document.getElementById("overflow-chat");
objDiv.scrollTop = objDiv.scrollHeight;
}
hljs.highlightAll();
setTimeout(function () {
if (window.innerWidth < 768) {
window.scrollTo(0, document.documentElement.scrollHeight);
}
}, 100);
}
/**
* Enables the chat input by setting the appropriate attributes and focusing on the chat input box.
*/
function enableChat() {
$(".character-typing").css('visibility', 'hidden')
$(".btn-send-chat,#chat").attr("disabled", false);
$(".btn-send-chat").show();
$(".btn-cancel-chat").hide();
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (!isMobile) {
setTimeout(function () {
$('#chat').focus();
}, 500);
}
}
/**
* Disables the chat input by setting the appropriate attributes and adjusting the visibility of certain elements.
*/
function disableChat() {
$(".character-typing").css('visibility', 'visible')
$(".character-typing").css('display', 'flex');
$(".character-typing span").html(prompts_name);
$(".btn-send-chat,#chat").attr("disabled", true);
$(".btn-send-chat").hide();
$(".btn-cancel-chat").show();
}
/**
* Creates a text file based on the data provided.
* @param {Array} data - An array containing chat data.
* @returns {Blob} A Blob object representing the text file.
*/
function createTextFile(data) {
let text = "";
// Iterate over the array_chat array and add each message to the text variable
data.shift();
data.forEach(chat => {
text += `${chat.name}: ${chat.message}\r\n`;
});
text = text.replace("User:", lang["translate"][lang_index].you + ":");
// Create a Blob object with the text
const blob = new Blob([text], { type: "text/plain" });
// Return the Blob object
return blob;
}
/**
* Generates and downloads a PDF document based on the chat messages.
*/
function downloadPdf() {
var docDefinition = {
content: [
{ text: lang["translate"][lang_index].header_title_pdf, style: 'header' },
"\n"
],
styles: {
header: {
fontSize: 22,
bold: true
},
name: {
fontSize: 14,
color: '#0072c6',
bold: true
},
message: {
fontSize: 12,
color: '#2c2c2c',
bold: false,
lineHeight: 1.2,
marginTop: 4
},
date: {
marginTop: 5,
fontSize: 10,
color: '#787878'
},
defaultStyle: {
font: 'Roboto'
}
}
};
// Adds each array element to the docDefinition
for (var i = 1; i < array_chat.length; i++) {
var message = array_chat[i];
var name = { text: message.name + ': ', style: 'name' };
var messageText = { text: message.message.replace(/[\u{1F300}-\u{1F6FF}\u{1F900}-\u{1F9FF}]/gu, ''), style: 'message' };
docDefinition.content.push(name);
docDefinition.content.push(messageText);
docDefinition.content.push({ text: message.date, style: 'date' });
docDefinition.content.push("\n");
}
// Create a pdfMake instance
var pdfMakeInstance = pdfMake.createPdf(docDefinition);
// Download pdf
pdfMakeInstance.download('chat.pdf');
}
/**
* Downloads a file with the provided Blob and filename.
* @param {Blob} blob - The Blob object to be downloaded.
* @param {string} fileName - The name of the file to be downloaded.
*/
function downloadFile(blob, fileName) {
// Create a URL object with the Blob
const url = URL.createObjectURL(blob);
// Create a download link and add it to the document
const link = document.createElement("a");
link.href = url;
link.download = fileName;
document.body.appendChild(link);
// Simulate a click on the link to trigger the download
link.click();
// Remove the link from the document
document.body.removeChild(link);
}
/**
* Handles the download button click event.
*/
function handleDownload() {
const blob = createTextFile(array_chat);
downloadFile(blob, "chat.txt");
}
/**
* Handles the chat audio functionality.
*/
$(document).on("click", ".chat-audio", function () {
var $this = $(this);
var $img = $this.find("img");
var $chatResponse = $this.siblings(".message-text").find(".chat-response")
var play = $img.attr("data-play") == "true";
if (play) {
cancelSpeechSynthesis();
}
$img.attr({
"src": "img/btn_tts_" + (play ? "play" : "stop") + ".svg",
"data-play": play ? "false" : "true"
});
if (!play) {
cancelSpeechSynthesis();
// Remove the text copy button before synthesizing speech
var chatResponseText = $chatResponse.html().replace(/