Update return types as optional via named arg - Update the modified slash commands for chat sending to use the named arg - Add `slashCommandReturnHelper` for shared funcitonality on return type usage

d8379edee7d8fb0a99e742aa929dc02602490860

Wolfsblvt <wolfsblvt@gmail.com>

3 files changed, +106 -6Ignore whitespace
public/scripts/slash-commands.js+31 -6
@@ -71,6 +71,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
71import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js';71import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js';
72import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';72import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
73import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';73import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
74import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
74export {75export {
75 executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,76 executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
76};77};
@@ -176,7 +177,7 @@ export function initDefaultSlashCommands() {
176 SlashCommandParser.addCommandObject(SlashCommand.fromProps({177 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
177 name: 'sendas',178 name: 'sendas',
178 callback: sendMessageAs,179 callback: sendMessageAs,
179 returns: 'The text of the sent message',180 returns: 'Optionally the text of the sent message, if specified in the "return" argument',
180 namedArgumentList: [181 namedArgumentList: [
181 SlashCommandNamedArgument.fromProps({182 SlashCommandNamedArgument.fromProps({
182 name: 'name',183 name: 'name',
@@ -195,6 +196,14 @@ export function initDefaultSlashCommands() {
195 typeList: [ARGUMENT_TYPE.NUMBER],196 typeList: [ARGUMENT_TYPE.NUMBER],
196 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),197 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
197 }),198 }),
199 SlashCommandNamedArgument.fromProps({
200 name: 'return',
201 description: 'The way how you want the return value to be provided',
202 typeList: [ARGUMENT_TYPE.STRING],
203 defaultValue: 'none',
204 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
205 forceEnum: true,
206 }),
198 ],207 ],
199 unnamedArgumentList: [208 unnamedArgumentList: [
200 new SlashCommandArgument(209 new SlashCommandArgument(
@@ -223,7 +232,7 @@ export function initDefaultSlashCommands() {
223 name: 'sys',232 name: 'sys',
224 callback: sendNarratorMessage,233 callback: sendNarratorMessage,
225 aliases: ['nar'],234 aliases: ['nar'],
226 returns: 'The text of the sent message',235 returns: 'Optionally the text of the sent message, if specified in the "return" argument',
227 namedArgumentList: [236 namedArgumentList: [
228 new SlashCommandNamedArgument(237 new SlashCommandNamedArgument(
229 'compact',238 'compact',
@@ -239,6 +248,14 @@ export function initDefaultSlashCommands() {
239 typeList: [ARGUMENT_TYPE.NUMBER],248 typeList: [ARGUMENT_TYPE.NUMBER],
240 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),249 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
241 }),250 }),
251 SlashCommandNamedArgument.fromProps({
252 name: 'return',
253 description: 'The way how you want the return value to be provided',
254 typeList: [ARGUMENT_TYPE.STRING],
255 defaultValue: 'none',
256 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
257 forceEnum: true,
258 }),
242 ],259 ],
243 unnamedArgumentList: [260 unnamedArgumentList: [
244 new SlashCommandArgument(261 new SlashCommandArgument(
@@ -278,7 +295,7 @@ export function initDefaultSlashCommands() {
278 SlashCommandParser.addCommandObject(SlashCommand.fromProps({295 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
279 name: 'comment',296 name: 'comment',
280 callback: sendCommentMessage,297 callback: sendCommentMessage,
281 returns: 'The text of the sent message',298 returns: 'Optionally the text of the sent message, if specified in the "return" argument',
282 namedArgumentList: [299 namedArgumentList: [
283 new SlashCommandNamedArgument(300 new SlashCommandNamedArgument(
284 'compact',301 'compact',
@@ -294,6 +311,14 @@ export function initDefaultSlashCommands() {
294 typeList: [ARGUMENT_TYPE.NUMBER],311 typeList: [ARGUMENT_TYPE.NUMBER],
295 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),312 enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
296 }),313 }),
314 SlashCommandNamedArgument.fromProps({
315 name: 'return',
316 description: 'The way how you want the return value to be provided',
317 typeList: [ARGUMENT_TYPE.STRING],
318 defaultValue: 'none',
319 enumList: slashCommandReturnHelper.enumList({ allowObject: true }),
320 forceEnum: true,
321 }),
297 ],322 ],
298 unnamedArgumentList: [323 unnamedArgumentList: [
299 new SlashCommandArgument(324 new SlashCommandArgument(
@@ -3210,7 +3235,7 @@ export async function sendMessageAs(args, text) {
3210 await saveChatConditional();3235 await saveChatConditional();
3211 }3236 }
32123237
3213 return message.mes;3238 return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
3214}3239}
32153240
3216export async function sendNarratorMessage(args, text) {3241export async function sendNarratorMessage(args, text) {
@@ -3264,7 +3289,7 @@ export async function sendNarratorMessage(args, text) {
3264 await saveChatConditional();3289 await saveChatConditional();
3265 }3290 }
32663291
3267 return message.mes;3292 return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
3268}3293}
32693294
3270export async function promptQuietForLoudResponse(who, text) {3295export async function promptQuietForLoudResponse(who, text) {
@@ -3353,7 +3378,7 @@ async function sendCommentMessage(args, text) {
3353 await saveChatConditional();3378 await saveChatConditional();
3354 }3379 }
33553380
3356 return message.mes;3381 return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes });
3357}3382}
33583383
3359/**3384/**
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+1 -0
@@ -36,6 +36,7 @@ export const enumIcons = {
36 message: '💬',36 message: '💬',
37 voice: '🎤',37 voice: '🎤',
38 server: '🖥️',38 server: '🖥️',
39 popup: '🗔',
3940
40 true: '✔️',41 true: '✔️',
41 false: '❌',42 false: '❌',
public/scripts/slash-commands/SlashCommandReturnHelper.js+74 -0
@@ -0,0 +1,74 @@
1import { sendSystemMessage, system_message_types } from '../../script.js';
2import { callGenericPopup, POPUP_TYPE } from '../popup.js';
3import { escapeHtml } from '../utils.js';
4import { enumIcons } from './SlashCommandCommonEnumsProvider.js';
5import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js';
6
7/** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */
8
9export const slashCommandReturnHelper = {
10 /**
11 * Gets/creates the enum list of types of return relevant for a slash command
12 *
13 * @param {object} [options={}] Options
14 * @param {boolean} [options.allowPipe=true] Allow option to pipe the return value
15 * @param {boolean} [options.allowObject=false] Allow option to return the value as an object
16 * @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message
17 * @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup
18 * @returns {SlashCommandEnumValue[]} The enum list
19 */
20 enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false } = {}) => [
21 allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'),
22 allowObject && new SlashCommandEnumValue('object', 'Return as an object to the pipe for the next command', enumTypes.variable, enumIcons.dictionary),
23 allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message),
24 allowChat && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message),
25 new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup),
26 new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup),
27 new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'),
28 new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'),
29 new SlashCommandEnumValue('console', 'Log the return value to the console', enumTypes.enum, '>'),
30 new SlashCommandEnumValue('none', 'No return value'),
31 ].filter(x => !!x),
32
33 /**
34 * Handles the return value based on the specified type
35 *
36 * @param {SlashCommandReturnType} type The type of return
37 * @param {object|number|string} value The value to return
38 * @param {object} [options={}] Options
39 * @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
40 * @returns {Promise<*>} The processed return value
41 */
42 async doReturn(type, value, { objectToStringFunc = o => o?.toString() } = {}) {
43 const stringValue = typeof value !== 'string' ? objectToStringFunc(value) : value;
44
45 switch (type) {
46 case 'popup-html':
47 case 'popup-text':
48 case 'chat-text':
49 case 'chat-html':
50 case 'toast-text':
51 case 'toast-html': {
52 const shouldHtml = type.endsWith('html');
53 const makeHtml = (str) => (new showdown.Converter()).makeHtml(str);
54
55 if (type.startsWith('popup')) await callGenericPopup(shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue), POPUP_TYPE.TEXT);
56 if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, shouldHtml ? makeHtml(stringValue) : escapeHtml(stringValue));
57 if (type.startsWith('toast')) toastr.info(stringValue, null, { escapeHtml: !shouldHtml }); // Toastr handles HTML conversion internally already
58
59 return '';
60 }
61 case 'pipe':
62 return stringValue ?? '';
63 case 'object':
64 return JSON.stringify(value);
65 case 'console':
66 console.info(value);
67 return '';
68 case 'none':
69 return '';
70 default:
71 throw new Error(`Unknown return type: ${type}`);
72 }
73 },
74};