import { saveSettingsDebounced } from "../../../script.js"; import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js"; export { MODULE_NAME }; const MODULE_NAME = 'expressions'; const UPDATE_INTERVAL = 1000; const DEFAULT_EXPRESSIONS = ['anger', 'fear', 'joy', 'love', 'sadness', 'surprise']; let expressionsList = null; let lastCharacter = undefined; let lastMessage = null; let inApiCall = false; function onExpressionsShowDefaultInput() { const value = $(this).prop('checked'); extension_settings.expressions.showDefault = value; saveSettingsDebounced(); const existingImageSrc = $('img.expression').prop('src'); if (existingImageSrc !== undefined) { //if we have an image in src if (!value && existingImageSrc.includes('/img/default-expressions/')) { //and that image is from /img/ (default) $('img.expression').prop('src', ''); //remove it lastMessage = null; } if (value) { lastMessage = null; } } } async function moduleWorker() { function getLastCharacterMessage() { const reversedChat = context.chat.slice().reverse(); for (let mes of reversedChat) { if (mes.is_user || mes.is_system) { continue; } return mes.mes; } return ''; } const context = getContext(); // group chats and non-characters not supported if (context.groupId || !context.characterId) { removeExpression(); return; } // character changed if (lastCharacter !== context.characterId) { removeExpression(); validateImages(); } if (!modules.includes('classify')) { $('.expression_settings').show(); $('.expression_settings .offline_mode').css('display', 'block'); lastCharacter = context.characterId; return; } else { $('.expression_settings .offline_mode').css('display', 'none'); } // check if last message changed const currentLastMessage = getLastCharacterMessage(); if (lastCharacter === context.characterId && lastMessage === currentLastMessage && $('img.expression').attr('src')) { return; } // API is busy if (inApiCall) { return; } try { inApiCall = true; const url = new URL(getApiUrl()); url.pathname = '/api/classify'; const apiResult = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Bypass-Tunnel-Reminder': 'bypass', }, body: JSON.stringify({ text: currentLastMessage }) }); if (apiResult.ok) { const data = await apiResult.json(); const expression = data.classification[0].label; setExpression(context.name2, expression); } } catch (error) { console.log(error); } finally { inApiCall = false; lastCharacter = context.characterId; lastMessage = currentLastMessage; } } function removeExpression() { lastMessage = null; $('img.expression').prop('src', ''); $('img.expression').removeClass('default'); $('.expression_settings').hide(); } let imagesValidating = false; async function validateImages() { if (imagesValidating) { return; } imagesValidating = true; const context = getContext(); $('.expression_settings').show(); $('#image_list').empty(); if (!context.characterId) { imagesValidating = false; return; } const IMAGE_LIST = (await getExpressionsList()).map(x => `${x}.png`); IMAGE_LIST.forEach((item) => { const image = document.createElement('img'); image.src = `/characters/${context.name2}/${item}`; image.classList.add('debug-image'); image.width = '0px'; image.height = '0px'; image.onload = function () { $('#image_list').append(getListItem(item, image.src, 'success')); } image.onerror = function () { $('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure')); } $('#image_list').prepend(image); }); imagesValidating = false; } function getListItem(item, imageSrc, textClass) { return `
You are in offline mode. Click on the image below to set the expression.
Hint: Create new folder in the public/characters/ folder and name it as the name of the character. Put PNG images with expressions there.