Merge pull request #2922 from SillyTavern/send-commands-return-value
Update various sending commands to return a value
This commit is contained in:
commit
be103534e4
|
@ -4785,7 +4785,7 @@ export function removeMacros(str) {
|
|||
* @param {boolean} [compact] Send as a compact display message.
|
||||
* @param {string} [name] Name of the user sending the message. Defaults to name1.
|
||||
* @param {string} [avatar] Avatar of the user sending the message. Defaults to user_avatar.
|
||||
* @returns {Promise<void>} A promise that resolves when the message is inserted.
|
||||
* @returns {Promise<any>} A promise that resolves to the message when it is inserted.
|
||||
*/
|
||||
export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) {
|
||||
messageText = getRegexedString(messageText, regex_placement.USER_INPUT);
|
||||
|
@ -4832,6 +4832,8 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul
|
|||
await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id);
|
||||
await saveChatConditional();
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,8 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
|
|||
import { isFunctionCallingSupported } from '../../openai.js';
|
||||
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
|
||||
import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js';
|
||||
import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js';
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'expressions';
|
||||
|
@ -2134,18 +2136,42 @@ function migrateSettings() {
|
|||
name: 'classify-expressions',
|
||||
aliases: ['expressions'],
|
||||
callback: async (args) => {
|
||||
const list = await getExpressionsList();
|
||||
switch (String(args.format).toLowerCase()) {
|
||||
case 'json':
|
||||
return JSON.stringify(list);
|
||||
default:
|
||||
return list.join(', ');
|
||||
/** @type {import('../../slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */
|
||||
// @ts-ignore
|
||||
let returnType = args.return;
|
||||
|
||||
// Old legacy return type handling
|
||||
if (args.format) {
|
||||
toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning');
|
||||
const type = String(args?.format).toLowerCase().trim();
|
||||
switch (type) {
|
||||
case 'json':
|
||||
returnType = 'object';
|
||||
break;
|
||||
default:
|
||||
returnType = 'pipe';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual new return type handling
|
||||
const list = await getExpressionsList();
|
||||
|
||||
return await slashCommandReturnHelper.doReturn(returnType ?? 'pipe', list, { objectToStringFunc: list => list.join(', ') });
|
||||
},
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'pipe',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
// TODO remove some day
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'format',
|
||||
description: 'The format to return the list in: comma-separated plain text or JSON array. Default is plain text.',
|
||||
description: '!!! DEPRECATED - use "return" instead !!! The format to return the list in: comma-separated plain text or JSON array. Default is plain text.',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
enumList: [
|
||||
new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '),
|
||||
|
|
|
@ -70,6 +70,7 @@ import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
|
|||
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
|
||||
import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
|
||||
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
|
||||
export {
|
||||
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
|
||||
};
|
||||
|
@ -242,6 +243,7 @@ export function initDefaultSlashCommands() {
|
|||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'sendas',
|
||||
callback: sendMessageAs,
|
||||
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
|
@ -269,6 +271,14 @@ export function initDefaultSlashCommands() {
|
|||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'none',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
|
@ -301,6 +311,7 @@ export function initDefaultSlashCommands() {
|
|||
name: 'sys',
|
||||
callback: sendNarratorMessage,
|
||||
aliases: ['nar'],
|
||||
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument(
|
||||
'compact',
|
||||
|
@ -316,6 +327,14 @@ export function initDefaultSlashCommands() {
|
|||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'none',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
|
@ -355,6 +374,7 @@ export function initDefaultSlashCommands() {
|
|||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'comment',
|
||||
callback: sendCommentMessage,
|
||||
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument(
|
||||
'compact',
|
||||
|
@ -370,6 +390,14 @@ export function initDefaultSlashCommands() {
|
|||
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'none',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
|
@ -509,7 +537,7 @@ export function initDefaultSlashCommands() {
|
|||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'ask',
|
||||
callback: askCharacter,
|
||||
returns: 'the generated text',
|
||||
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
|
@ -518,6 +546,14 @@ export function initDefaultSlashCommands() {
|
|||
isRequired: true,
|
||||
enumProvider: commonEnumProviders.characters('character'),
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'pipe',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
|
@ -556,6 +592,7 @@ export function initDefaultSlashCommands() {
|
|||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'send',
|
||||
callback: sendUserMessageCallback,
|
||||
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||
namedArgumentList: [
|
||||
new SlashCommandNamedArgument(
|
||||
'compact',
|
||||
|
@ -578,6 +615,14 @@ export function initDefaultSlashCommands() {
|
|||
defaultValue: '{{user}}',
|
||||
enumProvider: commonEnumProviders.personas,
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'none',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
|
@ -1568,12 +1613,21 @@ export function initDefaultSlashCommands() {
|
|||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'listinjects',
|
||||
callback: listInjectsCallback,
|
||||
helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>format</code> argument to change the output format.',
|
||||
returns: 'JSON object of script injections',
|
||||
helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>return</code> argument to change the return type.',
|
||||
returns: 'Optionalls the JSON object of script injections',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'popup-html',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
// TODO remove some day
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'format',
|
||||
description: 'output format',
|
||||
description: '!!! DEPRECATED - use "return" instead !!! output format',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
forceEnum: true,
|
||||
|
@ -1842,37 +1896,43 @@ function injectCallback(args, value) {
|
|||
}
|
||||
|
||||
async function listInjectsCallback(args) {
|
||||
const type = String(args?.format).toLowerCase().trim();
|
||||
if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) {
|
||||
type !== 'none' && toastr.info('No script injections for the current chat');
|
||||
return JSON.stringify({});
|
||||
/** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */
|
||||
let returnType = args.return;
|
||||
|
||||
// Old legacy return type handling
|
||||
if (args.format) {
|
||||
toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning');
|
||||
const type = String(args?.format).toLowerCase().trim();
|
||||
if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) {
|
||||
type !== 'none' && toastr.info('No script injections for the current chat');
|
||||
}
|
||||
switch (type) {
|
||||
case 'none':
|
||||
returnType = 'none';
|
||||
break;
|
||||
case 'chat':
|
||||
returnType = 'chat-html';
|
||||
break;
|
||||
case 'popup':
|
||||
default:
|
||||
returnType = 'popup-html';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const injects = Object.entries(chat_metadata.script_injects)
|
||||
.map(([id, inject]) => {
|
||||
const position = Object.entries(extension_prompt_types);
|
||||
const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown';
|
||||
return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`;
|
||||
})
|
||||
.join('\n');
|
||||
// Now the actual new return type handling
|
||||
const buildTextValue = (injects) => {
|
||||
const injectsStr = Object.entries(injects)
|
||||
.map(([id, inject]) => {
|
||||
const position = Object.entries(extension_prompt_types);
|
||||
const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown';
|
||||
return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`;
|
||||
})
|
||||
.join('\n');
|
||||
return `### Script injections:\n${injectsStr || 'No script injections for the current chat'}`;
|
||||
};
|
||||
|
||||
const converter = new showdown.Converter();
|
||||
const messageText = `### Script injections:\n${injects}`;
|
||||
const htmlMessage = DOMPurify.sanitize(converter.makeHtml(messageText));
|
||||
|
||||
switch (type) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'chat':
|
||||
sendSystemMessage(system_message_types.GENERIC, htmlMessage);
|
||||
break;
|
||||
case 'popup':
|
||||
default:
|
||||
await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT);
|
||||
break;
|
||||
}
|
||||
|
||||
return JSON.stringify(chat_metadata.script_injects);
|
||||
return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects ?? {}, { objectToStringFunc: buildTextValue });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2559,7 +2619,7 @@ async function askCharacter(args, text) {
|
|||
// Not supported in group chats
|
||||
// TODO: Maybe support group chats?
|
||||
if (selected_group) {
|
||||
toastr.error('Cannot run /ask command in a group chat!');
|
||||
toastr.warning('Cannot run /ask command in a group chat!');
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -2633,7 +2693,9 @@ async function askCharacter(args, text) {
|
|||
}
|
||||
}
|
||||
|
||||
return askResult;
|
||||
const message = askResult ? chat[chat.length - 1] : null;
|
||||
|
||||
return await slashCommandReturnHelper.doReturn(args.return ?? 'pipe', message, { objectToStringFunc: x => x.mes });
|
||||
}
|
||||
|
||||
async function hideMessageCallback(_, arg) {
|
||||
|
@ -2908,7 +2970,7 @@ function findPersonaByName(name) {
|
|||
|
||||
async function sendUserMessageCallback(args, text) {
|
||||
if (!text) {
|
||||
console.warn('WARN: No text provided for /send command');
|
||||
toastr.warning('You must specify text to send');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2924,16 +2986,17 @@ async function sendUserMessageCallback(args, text) {
|
|||
insertAt = chat.length + insertAt;
|
||||
}
|
||||
|
||||
let message;
|
||||
if ('name' in args) {
|
||||
const name = args.name || '';
|
||||
const avatar = findPersonaByName(name) || user_avatar;
|
||||
await sendMessageAsUser(text, bias, insertAt, compact, name, avatar);
|
||||
message = await sendMessageAsUser(text, bias, insertAt, compact, name, avatar);
|
||||
}
|
||||
else {
|
||||
await sendMessageAsUser(text, bias, insertAt, compact);
|
||||
message = await sendMessageAsUser(text, bias, insertAt, compact);
|
||||
}
|
||||
|
||||
return '';
|
||||
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||
}
|
||||
|
||||
async function deleteMessagesByNameCallback(_, name) {
|
||||
|
@ -3221,30 +3284,20 @@ export function getNameAndAvatarForMessage(character, name = null) {
|
|||
|
||||
export async function sendMessageAs(args, text) {
|
||||
if (!text) {
|
||||
toastr.warning('You must specify text to send as');
|
||||
return '';
|
||||
}
|
||||
|
||||
let name;
|
||||
let name = args.name?.trim();
|
||||
let mesText;
|
||||
|
||||
if (args.name) {
|
||||
name = args.name.trim();
|
||||
|
||||
if (!name && !text) {
|
||||
toastr.warning('You must specify a name and text to send as');
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
if (!name) {
|
||||
const namelessWarningKey = 'sendAsNamelessWarningShown';
|
||||
if (localStorage.getItem(namelessWarningKey) !== 'true') {
|
||||
toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });
|
||||
localStorage.setItem(namelessWarningKey, 'true');
|
||||
}
|
||||
name = name2;
|
||||
if (!text) {
|
||||
toastr.warning('You must specify text to send as');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
mesText = text.trim();
|
||||
|
@ -3321,11 +3374,12 @@ export async function sendMessageAs(args, text) {
|
|||
await saveChatConditional();
|
||||
}
|
||||
|
||||
return '';
|
||||
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||
}
|
||||
|
||||
export async function sendNarratorMessage(args, text) {
|
||||
if (!text) {
|
||||
toastr.warning('You must specify text to send');
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -3374,7 +3428,7 @@ export async function sendNarratorMessage(args, text) {
|
|||
await saveChatConditional();
|
||||
}
|
||||
|
||||
return '';
|
||||
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||
}
|
||||
|
||||
export async function promptQuietForLoudResponse(who, text) {
|
||||
|
@ -3420,6 +3474,7 @@ export async function promptQuietForLoudResponse(who, text) {
|
|||
|
||||
async function sendCommentMessage(args, text) {
|
||||
if (!text) {
|
||||
toastr.warning('You must specify text to send');
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -3462,7 +3517,7 @@ async function sendCommentMessage(args, text) {
|
|||
await saveChatConditional();
|
||||
}
|
||||
|
||||
return '';
|
||||
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,6 +36,7 @@ export const enumIcons = {
|
|||
message: '💬',
|
||||
voice: '🎤',
|
||||
server: '🖥️',
|
||||
popup: '🗔',
|
||||
|
||||
true: '✔️',
|
||||
false: '❌',
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import { sendSystemMessage, system_message_types } from '../../script.js';
|
||||
import { callGenericPopup, POPUP_TYPE } from '../popup.js';
|
||||
import { escapeHtml } from '../utils.js';
|
||||
import { enumIcons } from './SlashCommandCommonEnumsProvider.js';
|
||||
import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js';
|
||||
|
||||
/** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */
|
||||
|
||||
export const slashCommandReturnHelper = {
|
||||
// Without this, VSCode formatter fucks up JS docs. Don't ask me why.
|
||||
_: false,
|
||||
|
||||
/**
|
||||
* Gets/creates the enum list of types of return relevant for a slash command
|
||||
*
|
||||
* @param {object} [options={}] Options
|
||||
* @param {boolean} [options.allowPipe=true] Allow option to pipe the return value
|
||||
* @param {boolean} [options.allowObject=false] Allow option to return the value as an object
|
||||
* @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message
|
||||
* @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup
|
||||
* @param {boolean}[options.allowTextVersion=true] Used in combination with chat/popup/toast, some of them do not make sense for text versions, e.g.if you are building a HTML string anyway
|
||||
* @returns {SlashCommandEnumValue[]} The enum list
|
||||
*/
|
||||
enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false, allowTextVersion = true } = {}) => [
|
||||
allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'),
|
||||
allowObject && new SlashCommandEnumValue('object', 'Return as an object (or array) to the pipe for the next command', enumTypes.variable, enumIcons.dictionary),
|
||||
allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message),
|
||||
allowChat && allowTextVersion && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message),
|
||||
allowPopup && new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup),
|
||||
allowPopup && allowTextVersion && new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup),
|
||||
new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'),
|
||||
allowTextVersion && new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'),
|
||||
new SlashCommandEnumValue('console', 'Log the return value (object, if it can be one) to the console', enumTypes.enum, '>'),
|
||||
new SlashCommandEnumValue('none', 'No return value'),
|
||||
].filter(x => !!x),
|
||||
|
||||
/**
|
||||
* Handles the return value based on the specified type
|
||||
*
|
||||
* @param {SlashCommandReturnType} type The type of return
|
||||
* @param {object|number|string} value The value to return
|
||||
* @param {object} [options={}] Options
|
||||
* @param {(o: object) => string} [options.objectToStringFunc=null] Function to convert the object to a string, if object was provided and 'object' was not the chosen return type
|
||||
* @param {(o: object) => string} [options.objectToHtmlFunc=null] Analog to 'objectToStringFunc', which will be used here if not provided - but can do a different string layout if HTML is requested
|
||||
* @returns {Promise<*>} The processed return value
|
||||
*/
|
||||
async doReturn(type, value, { objectToStringFunc = o => o?.toString(), objectToHtmlFunc = null } = {}) {
|
||||
const shouldHtml = type.endsWith('html');
|
||||
const actualConverterFunc = shouldHtml && objectToHtmlFunc ? objectToHtmlFunc : objectToStringFunc;
|
||||
const stringValue = typeof value !== 'string' ? actualConverterFunc(value) : value;
|
||||
|
||||
switch (type) {
|
||||
case 'popup-html':
|
||||
case 'popup-text':
|
||||
case 'chat-text':
|
||||
case 'chat-html':
|
||||
case 'toast-text':
|
||||
case 'toast-html': {
|
||||
const htmlOrNotHtml = shouldHtml ? DOMPurify.sanitize((new showdown.Converter()).makeHtml(stringValue)) : escapeHtml(stringValue);
|
||||
|
||||
if (type.startsWith('popup')) await callGenericPopup(htmlOrNotHtml, POPUP_TYPE.TEXT);
|
||||
if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, htmlOrNotHtml);
|
||||
if (type.startsWith('toast')) toastr.info(htmlOrNotHtml, null, { escapeHtml: !shouldHtml });
|
||||
|
||||
return '';
|
||||
}
|
||||
case 'pipe':
|
||||
return stringValue ?? '';
|
||||
case 'object':
|
||||
return JSON.stringify(value);
|
||||
case 'console':
|
||||
console.info(value);
|
||||
return '';
|
||||
case 'none':
|
||||
return '';
|
||||
default:
|
||||
throw new Error(`Unknown return type: ${type}`);
|
||||
}
|
||||
},
|
||||
};
|
|
@ -11,6 +11,7 @@ import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureR
|
|||
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
|
||||
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
|
||||
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
|
||||
import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js';
|
||||
|
||||
|
@ -305,7 +306,28 @@ export function replaceVariableMacros(input) {
|
|||
}
|
||||
|
||||
async function listVariablesCallback(args) {
|
||||
const type = String(args?.format || '').toLowerCase().trim() || 'popup';
|
||||
/** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */
|
||||
let returnType = args.return;
|
||||
|
||||
// Old legacy return type handling
|
||||
if (args.format) {
|
||||
toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning');
|
||||
const type = String(args?.format).toLowerCase().trim();
|
||||
switch (type) {
|
||||
case 'none':
|
||||
returnType = 'none';
|
||||
break;
|
||||
case 'chat':
|
||||
returnType = 'chat-html';
|
||||
break;
|
||||
case 'popup':
|
||||
default:
|
||||
returnType = 'popup-html';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual new return type handling
|
||||
const scope = String(args?.scope || '').toLowerCase().trim() || 'all';
|
||||
if (!chat_metadata.variables) {
|
||||
chat_metadata.variables = {};
|
||||
|
@ -317,35 +339,24 @@ async function listVariablesCallback(args) {
|
|||
const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : [];
|
||||
const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : [];
|
||||
|
||||
const buildTextValue = (_) => {
|
||||
const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables';
|
||||
const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables';
|
||||
const chatName = getCurrentChatId();
|
||||
|
||||
const message = [
|
||||
includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '',
|
||||
includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '',
|
||||
].filter(x => x).join('\n\n');
|
||||
return message;
|
||||
};
|
||||
|
||||
const jsonVariables = [
|
||||
...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })),
|
||||
...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })),
|
||||
];
|
||||
|
||||
const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables';
|
||||
const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables';
|
||||
const chatName = getCurrentChatId();
|
||||
|
||||
const converter = new showdown.Converter();
|
||||
const message = [
|
||||
includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '',
|
||||
includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '',
|
||||
].filter(x => x).join('\n\n');
|
||||
const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message));
|
||||
|
||||
switch (type) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'chat':
|
||||
sendSystemMessage(system_message_types.GENERIC, htmlMessage);
|
||||
break;
|
||||
case 'popup':
|
||||
default:
|
||||
await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT);
|
||||
break;
|
||||
}
|
||||
|
||||
return JSON.stringify(jsonVariables);
|
||||
return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', jsonVariables, { objectToStringFunc: buildTextValue });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -916,7 +927,7 @@ export function registerVariableCommands() {
|
|||
name: 'listvar',
|
||||
callback: listVariablesCallback,
|
||||
aliases: ['listchatvar'],
|
||||
helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>format</code> argument to change the output format.',
|
||||
helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>return</code> argument to change the return type.',
|
||||
returns: 'JSON list of local variables',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
|
@ -932,9 +943,18 @@ export function registerVariableCommands() {
|
|||
new SlashCommandEnumValue('global', 'Global variables', enumTypes.enum, enumIcons.globalVariable),
|
||||
],
|
||||
}),
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'return',
|
||||
description: 'The way how you want the return value to be provided',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
defaultValue: 'popup-html',
|
||||
enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }),
|
||||
forceEnum: true,
|
||||
}),
|
||||
// TODO remove some day
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'format',
|
||||
description: 'output format',
|
||||
description: '!!! DEPRECATED - use "return" instead !!! output format',
|
||||
typeList: [ARGUMENT_TYPE.STRING],
|
||||
isRequired: true,
|
||||
forceEnum: true,
|
||||
|
|
Loading…
Reference in New Issue