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 {boolean} [compact] Send as a compact display message.
|
||||||
* @param {string} [name] Name of the user sending the message. Defaults to name1.
|
* @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.
|
* @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) {
|
export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) {
|
||||||
messageText = getRegexedString(messageText, regex_placement.USER_INPUT);
|
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 eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id);
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
|
||||||
import { isFunctionCallingSupported } from '../../openai.js';
|
import { isFunctionCallingSupported } from '../../openai.js';
|
||||||
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
|
import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
|
||||||
import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.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 };
|
export { MODULE_NAME };
|
||||||
|
|
||||||
const MODULE_NAME = 'expressions';
|
const MODULE_NAME = 'expressions';
|
||||||
|
@ -2134,18 +2136,42 @@ function migrateSettings() {
|
||||||
name: 'classify-expressions',
|
name: 'classify-expressions',
|
||||||
aliases: ['expressions'],
|
aliases: ['expressions'],
|
||||||
callback: async (args) => {
|
callback: async (args) => {
|
||||||
const list = await getExpressionsList();
|
/** @type {import('../../slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */
|
||||||
switch (String(args.format).toLowerCase()) {
|
// @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':
|
case 'json':
|
||||||
return JSON.stringify(list);
|
returnType = 'object';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return list.join(', ');
|
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: [
|
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({
|
SlashCommandNamedArgument.fromProps({
|
||||||
name: 'format',
|
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],
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
enumList: [
|
enumList: [
|
||||||
new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '),
|
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 { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||||
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
|
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
|
||||||
import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
|
import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
|
||||||
|
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
|
||||||
export {
|
export {
|
||||||
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
|
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
|
||||||
};
|
};
|
||||||
|
@ -242,6 +243,7 @@ export function initDefaultSlashCommands() {
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
name: 'sendas',
|
name: 'sendas',
|
||||||
callback: sendMessageAs,
|
callback: sendMessageAs,
|
||||||
|
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
SlashCommandNamedArgument.fromProps({
|
SlashCommandNamedArgument.fromProps({
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
@ -269,6 +271,14 @@ export function initDefaultSlashCommands() {
|
||||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
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: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
|
@ -301,6 +311,7 @@ export function initDefaultSlashCommands() {
|
||||||
name: 'sys',
|
name: 'sys',
|
||||||
callback: sendNarratorMessage,
|
callback: sendNarratorMessage,
|
||||||
aliases: ['nar'],
|
aliases: ['nar'],
|
||||||
|
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'compact',
|
'compact',
|
||||||
|
@ -316,6 +327,14 @@ export function initDefaultSlashCommands() {
|
||||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
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: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
|
@ -355,6 +374,7 @@ export function initDefaultSlashCommands() {
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
name: 'comment',
|
name: 'comment',
|
||||||
callback: sendCommentMessage,
|
callback: sendCommentMessage,
|
||||||
|
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'compact',
|
'compact',
|
||||||
|
@ -370,6 +390,14 @@ export function initDefaultSlashCommands() {
|
||||||
typeList: [ARGUMENT_TYPE.NUMBER],
|
typeList: [ARGUMENT_TYPE.NUMBER],
|
||||||
enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
|
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: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
|
@ -509,7 +537,7 @@ export function initDefaultSlashCommands() {
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
name: 'ask',
|
name: 'ask',
|
||||||
callback: askCharacter,
|
callback: askCharacter,
|
||||||
returns: 'the generated text',
|
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
SlashCommandNamedArgument.fromProps({
|
SlashCommandNamedArgument.fromProps({
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
@ -518,6 +546,14 @@ export function initDefaultSlashCommands() {
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
enumProvider: commonEnumProviders.characters('character'),
|
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: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
|
@ -556,6 +592,7 @@ export function initDefaultSlashCommands() {
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
name: 'send',
|
name: 'send',
|
||||||
callback: sendUserMessageCallback,
|
callback: sendUserMessageCallback,
|
||||||
|
returns: 'Optionally the text of the sent message, if specified in the "return" argument',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'compact',
|
'compact',
|
||||||
|
@ -578,6 +615,14 @@ export function initDefaultSlashCommands() {
|
||||||
defaultValue: '{{user}}',
|
defaultValue: '{{user}}',
|
||||||
enumProvider: commonEnumProviders.personas,
|
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: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
|
@ -1568,12 +1613,21 @@ export function initDefaultSlashCommands() {
|
||||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||||
name: 'listinjects',
|
name: 'listinjects',
|
||||||
callback: listInjectsCallback,
|
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.',
|
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: 'JSON object of script injections',
|
returns: 'Optionalls the JSON object of script injections',
|
||||||
namedArgumentList: [
|
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({
|
SlashCommandNamedArgument.fromProps({
|
||||||
name: 'format',
|
name: 'format',
|
||||||
description: 'output format',
|
description: '!!! DEPRECATED - use "return" instead !!! output format',
|
||||||
typeList: [ARGUMENT_TYPE.STRING],
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
forceEnum: true,
|
forceEnum: true,
|
||||||
|
@ -1842,37 +1896,43 @@ function injectCallback(args, value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listInjectsCallback(args) {
|
async function listInjectsCallback(args) {
|
||||||
|
/** @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();
|
const type = String(args?.format).toLowerCase().trim();
|
||||||
if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) {
|
if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) {
|
||||||
type !== 'none' && toastr.info('No script injections for the current chat');
|
type !== 'none' && toastr.info('No script injections for the current chat');
|
||||||
return JSON.stringify({});
|
}
|
||||||
|
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)
|
// Now the actual new return type handling
|
||||||
|
const buildTextValue = (injects) => {
|
||||||
|
const injectsStr = Object.entries(injects)
|
||||||
.map(([id, inject]) => {
|
.map(([id, inject]) => {
|
||||||
const position = Object.entries(extension_prompt_types);
|
const position = Object.entries(extension_prompt_types);
|
||||||
const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown';
|
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})`;
|
return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`;
|
||||||
})
|
})
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
return `### Script injections:\n${injectsStr || 'No script injections for the current chat'}`;
|
||||||
|
};
|
||||||
|
|
||||||
const converter = new showdown.Converter();
|
return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects ?? {}, { objectToStringFunc: buildTextValue });
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2559,7 +2619,7 @@ async function askCharacter(args, text) {
|
||||||
// Not supported in group chats
|
// Not supported in group chats
|
||||||
// TODO: Maybe support group chats?
|
// TODO: Maybe support group chats?
|
||||||
if (selected_group) {
|
if (selected_group) {
|
||||||
toastr.error('Cannot run /ask command in a group chat!');
|
toastr.warning('Cannot run /ask command in a group chat!');
|
||||||
return '';
|
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) {
|
async function hideMessageCallback(_, arg) {
|
||||||
|
@ -2908,7 +2970,7 @@ function findPersonaByName(name) {
|
||||||
|
|
||||||
async function sendUserMessageCallback(args, text) {
|
async function sendUserMessageCallback(args, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
console.warn('WARN: No text provided for /send command');
|
toastr.warning('You must specify text to send');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2924,16 +2986,17 @@ async function sendUserMessageCallback(args, text) {
|
||||||
insertAt = chat.length + insertAt;
|
insertAt = chat.length + insertAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let message;
|
||||||
if ('name' in args) {
|
if ('name' in args) {
|
||||||
const name = args.name || '';
|
const name = args.name || '';
|
||||||
const avatar = findPersonaByName(name) || user_avatar;
|
const avatar = findPersonaByName(name) || user_avatar;
|
||||||
await sendMessageAsUser(text, bias, insertAt, compact, name, avatar);
|
message = await sendMessageAsUser(text, bias, insertAt, compact, name, avatar);
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
async function deleteMessagesByNameCallback(_, name) {
|
||||||
|
@ -3221,30 +3284,20 @@ export function getNameAndAvatarForMessage(character, name = null) {
|
||||||
|
|
||||||
export async function sendMessageAs(args, text) {
|
export async function sendMessageAs(args, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
|
toastr.warning('You must specify text to send as');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
let name;
|
let name = args.name?.trim();
|
||||||
let mesText;
|
let mesText;
|
||||||
|
|
||||||
if (args.name) {
|
if (!name) {
|
||||||
name = args.name.trim();
|
|
||||||
|
|
||||||
if (!name && !text) {
|
|
||||||
toastr.warning('You must specify a name and text to send as');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const namelessWarningKey = 'sendAsNamelessWarningShown';
|
const namelessWarningKey = 'sendAsNamelessWarningShown';
|
||||||
if (localStorage.getItem(namelessWarningKey) !== 'true') {
|
if (localStorage.getItem(namelessWarningKey) !== 'true') {
|
||||||
toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });
|
toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });
|
||||||
localStorage.setItem(namelessWarningKey, 'true');
|
localStorage.setItem(namelessWarningKey, 'true');
|
||||||
}
|
}
|
||||||
name = name2;
|
name = name2;
|
||||||
if (!text) {
|
|
||||||
toastr.warning('You must specify text to send as');
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mesText = text.trim();
|
mesText = text.trim();
|
||||||
|
@ -3321,11 +3374,12 @@ export async function sendMessageAs(args, text) {
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendNarratorMessage(args, text) {
|
export async function sendNarratorMessage(args, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
|
toastr.warning('You must specify text to send');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3374,7 +3428,7 @@ export async function sendNarratorMessage(args, text) {
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function promptQuietForLoudResponse(who, text) {
|
export async function promptQuietForLoudResponse(who, text) {
|
||||||
|
@ -3420,6 +3474,7 @@ export async function promptQuietForLoudResponse(who, text) {
|
||||||
|
|
||||||
async function sendCommentMessage(args, text) {
|
async function sendCommentMessage(args, text) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
|
toastr.warning('You must specify text to send');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3462,7 +3517,7 @@ async function sendCommentMessage(args, text) {
|
||||||
await saveChatConditional();
|
await saveChatConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,6 +36,7 @@ export const enumIcons = {
|
||||||
message: '💬',
|
message: '💬',
|
||||||
voice: '🎤',
|
voice: '🎤',
|
||||||
server: '🖥️',
|
server: '🖥️',
|
||||||
|
popup: '🗔',
|
||||||
|
|
||||||
true: '✔️',
|
true: '✔️',
|
||||||
false: '❌',
|
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 { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||||
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
|
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
|
||||||
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.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 { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
|
||||||
import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js';
|
import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js';
|
||||||
|
|
||||||
|
@ -305,7 +306,28 @@ export function replaceVariableMacros(input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listVariablesCallback(args) {
|
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';
|
const scope = String(args?.scope || '').toLowerCase().trim() || 'all';
|
||||||
if (!chat_metadata.variables) {
|
if (!chat_metadata.variables) {
|
||||||
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 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 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 = [
|
const jsonVariables = [
|
||||||
...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })),
|
...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' })),
|
...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';
|
return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', jsonVariables, { objectToStringFunc: buildTextValue });
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -916,7 +927,7 @@ export function registerVariableCommands() {
|
||||||
name: 'listvar',
|
name: 'listvar',
|
||||||
callback: listVariablesCallback,
|
callback: listVariablesCallback,
|
||||||
aliases: ['listchatvar'],
|
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',
|
returns: 'JSON list of local variables',
|
||||||
namedArgumentList: [
|
namedArgumentList: [
|
||||||
SlashCommandNamedArgument.fromProps({
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
@ -932,9 +943,18 @@ export function registerVariableCommands() {
|
||||||
new SlashCommandEnumValue('global', 'Global variables', enumTypes.enum, enumIcons.globalVariable),
|
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({
|
SlashCommandNamedArgument.fromProps({
|
||||||
name: 'format',
|
name: 'format',
|
||||||
description: 'output format',
|
description: '!!! DEPRECATED - use "return" instead !!! output format',
|
||||||
typeList: [ARGUMENT_TYPE.STRING],
|
typeList: [ARGUMENT_TYPE.STRING],
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
forceEnum: true,
|
forceEnum: true,
|
||||||
|
|
Loading…
Reference in New Issue