chore(wi): Add logSlashCommandWarn utility and integrate into world-info slash commands (#5096)
Signed| @@ -2752,6 +2752,33 @@ export function versionCompare(srcVersion, minVersion) { | |||
| 2752 | } | 2752 | } |
| 2753 | 2753 | ||
| 2754 | /** | 2754 | /** |
| 2755 | * Logs a warning to the console for slash command executions. | ||
| 2756 | * Strips internal arguments (starting with '_') from the args object for cleaner logging. | ||
| 2757 | * @param {string} message - The warning message to log. | ||
| 2758 | * @param {Object} args - The arguments object from the slash command, including named arguments and internal values. | ||
| 2759 | * @param {{[unnamedArgName: string]: string}} [valueObj=null] - The user-built object containing context for the warning (e.g., { uid: uid }). | ||
| 2760 | * @returns {void} | ||
| 2761 | */ | ||
| 2762 | export function logSlashCommandWarn(message, args, valueObj = null) { | ||
| 2763 | if (valueObj !== null && valueObj !== undefined) { | ||
| 2764 | console.warn(message, valueObj, stripInternalArgs(args)); | ||
| 2765 | } else { | ||
| 2766 | console.warn(message, stripInternalArgs(args)); | ||
| 2767 | } | ||
| 2768 | return; | ||
| 2769 | function stripInternalArgs(args) { | ||
| 2770 | // strip all args/properties that start with an underscore | ||
| 2771 | const result = {}; | ||
| 2772 | for (const [key, value] of Object.entries(args)) { | ||
| 2773 | if (!key.startsWith('_')) { | ||
| 2774 | result[key] = value; | ||
| 2775 | } | ||
| 2776 | } | ||
| 2777 | return result; | ||
| 2778 | } | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | /** | ||
| 2755 | * Sets up the scroll-to-top button functionality. | 2782 | * Sets up the scroll-to-top button functionality. |
| 2756 | * @param {object} params Parameters object | 2783 | * @param {object} params Parameters object |
| 2757 | * @param {string} params.scrollContainerId Scrollable container element ID | 2784 | * @param {string} params.scrollContainerId Scrollable container element ID |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { Fuse } from '../lib.js'; | 1 | import { Fuse } from '../lib.js'; |
| 2 | 2 | ||
| 3 | import { saveSettings, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles, create_save, createOrEditCharacter, name1 } from '../script.js'; | 3 | import { saveSettings, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles, create_save, createOrEditCharacter, name1 } from '../script.js'; |
| 4 | import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, getSanitizedFilename, checkOverwriteExistingData, getStringHash, parseStringArray, cancelDebounce, findChar, onlyUnique, equalsIgnoreCaseAndAccents, uuidv4, normalizeArray, getUniqueName } from './utils.js'; | 4 | import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, getSanitizedFilename, checkOverwriteExistingData, getStringHash, parseStringArray, cancelDebounce, findChar, onlyUnique, equalsIgnoreCaseAndAccents, uuidv4, normalizeArray, getUniqueName, logSlashCommandWarn } from './utils.js'; |
| 5 | import { extension_settings, getContext } from './extensions.js'; | 5 | import { extension_settings, getContext } from './extensions.js'; |
| 6 | import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js'; | 6 | import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js'; |
| 7 | import { isMobile } from './RossAscends-mods.js'; | 7 | import { isMobile } from './RossAscends-mods.js'; |
| @@ -1059,9 +1059,10 @@ function registerWorldInfoSlashCommands() { | |||
| 1059 | return getContext().chat.filter(x => !x.is_system).map(x => x.mes); | 1059 | return getContext().chat.filter(x => !x.is_system).map(x => x.mes); |
| 1060 | } | 1060 | } |
| 1061 | 1061 | ||
| 1062 | async function getEntriesFromFile(file) { | 1062 | async function getEntriesFromFile(file, { args = {}, unnamed = null, callbackName = 'getEntriesFromFile' } = {}) { |
| 1063 | if (!file || !world_names.includes(file)) { | 1063 | if (!file || !world_names.includes(file)) { |
| 1064 | toastr.warning(t`Valid World Info file name is required`); | 1064 | toastr.warning(t`Valid World Info file name is required`); |
| 1065 | logSlashCommandWarn(`${callbackName}: Valid World Info file name is required`, args, unnamed); | ||
| 1065 | return ''; | 1066 | return ''; |
| 1066 | } | 1067 | } |
| 1067 | 1068 | ||
| @@ -1069,6 +1070,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1069 | 1070 | ||
| 1070 | if (!data || !('entries' in data)) { | 1071 | if (!data || !('entries' in data)) { |
| 1071 | toastr.warning(t`World Info file has an invalid format`); | 1072 | toastr.warning(t`World Info file has an invalid format`); |
| 1073 | logSlashCommandWarn(`${callbackName}: World Info file has an invalid format`, args, unnamed); | ||
| 1072 | return ''; | 1074 | return ''; |
| 1073 | } | 1075 | } |
| 1074 | 1076 | ||
| @@ -1076,6 +1078,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1076 | 1078 | ||
| 1077 | if (!entries || entries.length === 0) { | 1079 | if (!entries || entries.length === 0) { |
| 1078 | toastr.warning(t`World Info file has no entries`); | 1080 | toastr.warning(t`World Info file has no entries`); |
| 1081 | logSlashCommandWarn(`${callbackName}: World Info file has no entries`, args, unnamed); | ||
| 1079 | return ''; | 1082 | return ''; |
| 1080 | } | 1083 | } |
| 1081 | 1084 | ||
| @@ -1119,6 +1122,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1119 | const character = findChar({ name: characterIdentifier }); | 1122 | const character = findChar({ name: characterIdentifier }); |
| 1120 | if (!character) { | 1123 | if (!character) { |
| 1121 | toastr.error(t`Character not found.`); | 1124 | toastr.error(t`Character not found.`); |
| 1125 | logSlashCommandWarn('getCharBookCallback: Character not found', { type, name, create }, { characterIdentifier }); | ||
| 1122 | return ''; | 1126 | return ''; |
| 1123 | } | 1127 | } |
| 1124 | const books = []; | 1128 | const books = []; |
| @@ -1160,6 +1164,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1160 | 1164 | ||
| 1161 | if (!chatId) { | 1165 | if (!chatId) { |
| 1162 | toastr.warning(t`Open a chat to get a name of the chat-bound lorebook`); | 1166 | toastr.warning(t`Open a chat to get a name of the chat-bound lorebook`); |
| 1167 | logSlashCommandWarn('getChatBookCallback: Open a chat to get a name of the chat-bound lorebook', args); | ||
| 1163 | return ''; | 1168 | return ''; |
| 1164 | } | 1169 | } |
| 1165 | 1170 | ||
| @@ -1205,7 +1210,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1205 | const file = args.file; | 1210 | const file = args.file; |
| 1206 | const field = args.field || 'key'; | 1211 | const field = args.field || 'key'; |
| 1207 | 1212 | ||
| 1208 | const entries = await getEntriesFromFile(file); | 1213 | const entries = await getEntriesFromFile(file, { args, unnamed: { value }, callbackName: 'findBookEntryCallback' }); |
| 1209 | 1214 | ||
| 1210 | if (!entries) { | 1215 | if (!entries) { |
| 1211 | return ''; | 1216 | return ''; |
| @@ -1250,7 +1255,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1250 | const field = args.field || 'content'; | 1255 | const field = args.field || 'content'; |
| 1251 | const tags = getContext().tags; | 1256 | const tags = getContext().tags; |
| 1252 | 1257 | ||
| 1253 | const entries = await getEntriesFromFile(file); | 1258 | const entries = await getEntriesFromFile(file, { args, unnamed: { uid }, callbackName: 'getEntryFieldCallback' }); |
| 1254 | 1259 | ||
| 1255 | if (!entries) { | 1260 | if (!entries) { |
| 1256 | return ''; | 1261 | return ''; |
| @@ -1260,11 +1265,14 @@ function registerWorldInfoSlashCommands() { | |||
| 1260 | 1265 | ||
| 1261 | if (!entry) { | 1266 | if (!entry) { |
| 1262 | toastr.warning('Valid UID is required'); | 1267 | toastr.warning('Valid UID is required'); |
| 1268 | logSlashCommandWarn('getEntryFieldCallback: Valid UID is required', args, { uid }); | ||
| 1269 | console.warn(); | ||
| 1263 | return ''; | 1270 | return ''; |
| 1264 | } | 1271 | } |
| 1265 | 1272 | ||
| 1266 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { | 1273 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { |
| 1267 | toastr.warning('Valid field name is required'); | 1274 | toastr.warning('Valid field name is required'); |
| 1275 | logSlashCommandWarn('getEntryFieldCallback: Valid field name is required', args, { uid }); | ||
| 1268 | return ''; | 1276 | return ''; |
| 1269 | } | 1277 | } |
| 1270 | 1278 | ||
| @@ -1313,6 +1321,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1313 | 1321 | ||
| 1314 | if (!data || !('entries' in data)) { | 1322 | if (!data || !('entries' in data)) { |
| 1315 | toastr.warning('Valid World Info file name is required'); | 1323 | toastr.warning('Valid World Info file name is required'); |
| 1324 | logSlashCommandWarn('createEntryCallback: Valid World Info file name is required', args); | ||
| 1316 | return ''; | 1325 | return ''; |
| 1317 | } | 1326 | } |
| 1318 | 1327 | ||
| @@ -1358,6 +1367,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1358 | 1367 | ||
| 1359 | if (value === undefined) { | 1368 | if (value === undefined) { |
| 1360 | toastr.warning('Value is required'); | 1369 | toastr.warning('Value is required'); |
| 1370 | logSlashCommandWarn('setEntryFieldCallback: Value is required', args, { value }); | ||
| 1361 | return ''; | 1371 | return ''; |
| 1362 | } | 1372 | } |
| 1363 | 1373 | ||
| @@ -1367,6 +1377,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1367 | 1377 | ||
| 1368 | if (!data || !('entries' in data)) { | 1378 | if (!data || !('entries' in data)) { |
| 1369 | toastr.warning('Valid World Info file name is required'); | 1379 | toastr.warning('Valid World Info file name is required'); |
| 1380 | logSlashCommandWarn('setEntryFieldCallback: Valid World Info file name is required', args, { value }); | ||
| 1370 | return ''; | 1381 | return ''; |
| 1371 | } | 1382 | } |
| 1372 | 1383 | ||
| @@ -1374,11 +1385,13 @@ function registerWorldInfoSlashCommands() { | |||
| 1374 | 1385 | ||
| 1375 | if (!entry) { | 1386 | if (!entry) { |
| 1376 | toastr.warning('Valid UID is required'); | 1387 | toastr.warning('Valid UID is required'); |
| 1388 | logSlashCommandWarn('setEntryFieldCallback: Valid UID is required', args, { value }); | ||
| 1377 | return ''; | 1389 | return ''; |
| 1378 | } | 1390 | } |
| 1379 | 1391 | ||
| 1380 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { | 1392 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { |
| 1381 | toastr.warning('Valid field name is required'); | 1393 | toastr.warning('Valid field name is required'); |
| 1394 | logSlashCommandWarn('setEntryFieldCallback: Valid field name is required', args, { value }); | ||
| 1382 | return ''; | 1395 | return ''; |
| 1383 | } | 1396 | } |
| 1384 | 1397 | ||
| @@ -1445,7 +1458,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1445 | const uid = value; | 1458 | const uid = value; |
| 1446 | const effect = args.effect; | 1459 | const effect = args.effect; |
| 1447 | 1460 | ||
| 1448 | const entries = await getEntriesFromFile(file); | 1461 | const entries = await getEntriesFromFile(file, { args, unnamed: { uid }, callbackName: 'getTimedEffectCallback' }); |
| 1449 | 1462 | ||
| 1450 | if (!entries) { | 1463 | if (!entries) { |
| 1451 | return ''; | 1464 | return ''; |
| @@ -1456,6 +1469,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1456 | 1469 | ||
| 1457 | if (!entry) { | 1470 | if (!entry) { |
| 1458 | toastr.warning('Valid UID is required'); | 1471 | toastr.warning('Valid UID is required'); |
| 1472 | logSlashCommandWarn('getTimedEffectCallback: Valid UID is required', args, { uid }); | ||
| 1459 | return ''; | 1473 | return ''; |
| 1460 | } | 1474 | } |
| 1461 | 1475 | ||
| @@ -1465,6 +1479,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1465 | 1479 | ||
| 1466 | if (!timedEffects.isValidEffectType(effect)) { | 1480 | if (!timedEffects.isValidEffectType(effect)) { |
| 1467 | toastr.warning('Valid effect type is required'); | 1481 | toastr.warning('Valid effect type is required'); |
| 1482 | logSlashCommandWarn('getTimedEffectCallback: Valid effect type is required', args, { uid }); | ||
| 1468 | return ''; | 1483 | return ''; |
| 1469 | } | 1484 | } |
| 1470 | 1485 | ||
| @@ -1488,10 +1503,11 @@ function registerWorldInfoSlashCommands() { | |||
| 1488 | 1503 | ||
| 1489 | if (value === undefined) { | 1504 | if (value === undefined) { |
| 1490 | toastr.warning('New state is required'); | 1505 | toastr.warning('New state is required'); |
| 1506 | logSlashCommandWarn('setTimedEffectCallback: New state is required', args, { value }); | ||
| 1491 | return ''; | 1507 | return ''; |
| 1492 | } | 1508 | } |
| 1493 | 1509 | ||
| 1494 | const entries = await getEntriesFromFile(file); | 1510 | const entries = await getEntriesFromFile(file, { args, unnamed: { value }, callbackName: 'setTimedEffectCallback' }); |
| 1495 | 1511 | ||
| 1496 | if (!entries) { | 1512 | if (!entries) { |
| 1497 | return ''; | 1513 | return ''; |
| @@ -1502,6 +1518,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1502 | 1518 | ||
| 1503 | if (!entry) { | 1519 | if (!entry) { |
| 1504 | toastr.warning('Valid UID is required'); | 1520 | toastr.warning('Valid UID is required'); |
| 1521 | logSlashCommandWarn('setTimedEffectCallback: Valid UID is required', args, { value }); | ||
| 1505 | return ''; | 1522 | return ''; |
| 1506 | } | 1523 | } |
| 1507 | 1524 | ||
| @@ -1511,11 +1528,13 @@ function registerWorldInfoSlashCommands() { | |||
| 1511 | 1528 | ||
| 1512 | if (!timedEffects.isValidEffectType(effect)) { | 1529 | if (!timedEffects.isValidEffectType(effect)) { |
| 1513 | toastr.warning('Valid effect type is required'); | 1530 | toastr.warning('Valid effect type is required'); |
| 1531 | logSlashCommandWarn('setTimedEffectCallback: Valid effect type is required', args, { value }); | ||
| 1514 | return ''; | 1532 | return ''; |
| 1515 | } | 1533 | } |
| 1516 | 1534 | ||
| 1517 | if (!entry[effect]) { | 1535 | if (!entry[effect]) { |
| 1518 | toastr.warning('This entry does not have the selected effect. Configure it in the editor first.'); | 1536 | toastr.warning('This entry does not have the selected effect. Configure it in the editor first.'); |
| 1537 | logSlashCommandWarn('setTimedEffectCallback: This entry does not have the selected effect', args, { value }); | ||
| 1519 | return ''; | 1538 | return ''; |
| 1520 | } | 1539 | } |
| 1521 | 1540 | ||