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 | 2782 | * Sets up the scroll-to-top button functionality. |
| 2756 | 2783 | * @param {object} params Parameters object |
| 2757 | 2784 | * @param {string} params.scrollContainerId Scrollable container element ID |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { Fuse } from '../lib.js'; |
| 2 | 2 | |
| 3 | 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 | 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 | 5 | import { extension_settings, getContext } from './extensions.js'; |
| 6 | 6 | import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js'; |
| 7 | 7 | import { isMobile } from './RossAscends-mods.js'; |
| @@ -1059,9 +1059,10 @@ function registerWorldInfoSlashCommands() { | ||
| 1059 | 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 | 1063 | if (!file || !world_names.includes(file)) { |
| 1064 | 1064 | toastr.warning(t`Valid World Info file name is required`); |
| 1065 | + logSlashCommandWarn(`${callbackName}: Valid World Info file name is required`, args, unnamed); | |
| 1065 | 1066 | return ''; |
| 1066 | 1067 | } |
| 1067 | 1068 | |
| @@ -1069,6 +1070,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1069 | 1070 | |
| 1070 | 1071 | if (!data || !('entries' in data)) { |
| 1071 | 1072 | toastr.warning(t`World Info file has an invalid format`); |
| 1073 | + logSlashCommandWarn(`${callbackName}: World Info file has an invalid format`, args, unnamed); | |
| 1072 | 1074 | return ''; |
| 1073 | 1075 | } |
| 1074 | 1076 | |
| @@ -1076,6 +1078,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1076 | 1078 | |
| 1077 | 1079 | if (!entries || entries.length === 0) { |
| 1078 | 1080 | toastr.warning(t`World Info file has no entries`); |
| 1081 | + logSlashCommandWarn(`${callbackName}: World Info file has no entries`, args, unnamed); | |
| 1079 | 1082 | return ''; |
| 1080 | 1083 | } |
| 1081 | 1084 | |
| @@ -1119,6 +1122,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1119 | 1122 | const character = findChar({ name: characterIdentifier }); |
| 1120 | 1123 | if (!character) { |
| 1121 | 1124 | toastr.error(t`Character not found.`); |
| 1125 | + logSlashCommandWarn('getCharBookCallback: Character not found', { type, name, create }, { characterIdentifier }); | |
| 1122 | 1126 | return ''; |
| 1123 | 1127 | } |
| 1124 | 1128 | const books = []; |
| @@ -1160,6 +1164,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1160 | 1164 | |
| 1161 | 1165 | if (!chatId) { |
| 1162 | 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 | 1168 | return ''; |
| 1164 | 1169 | } |
| 1165 | 1170 | |
| @@ -1205,7 +1210,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1205 | 1210 | const file = args.file; |
| 1206 | 1211 | const field = args.field || 'key'; |
| 1207 | 1212 | |
| 1208 | 1213 | const entries = await getEntriesFromFile(file, { args, unnamed: { value }, callbackName: 'findBookEntryCallback' }); |
| 1209 | 1214 | |
| 1210 | 1215 | if (!entries) { |
| 1211 | 1216 | return ''; |
| @@ -1250,7 +1255,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1250 | 1255 | const field = args.field || 'content'; |
| 1251 | 1256 | const tags = getContext().tags; |
| 1252 | 1257 | |
| 1253 | 1258 | const entries = await getEntriesFromFile(file, { args, unnamed: { uid }, callbackName: 'getEntryFieldCallback' }); |
| 1254 | 1259 | |
| 1255 | 1260 | if (!entries) { |
| 1256 | 1261 | return ''; |
| @@ -1260,11 +1265,14 @@ function registerWorldInfoSlashCommands() { | ||
| 1260 | 1265 | |
| 1261 | 1266 | if (!entry) { |
| 1262 | 1267 | toastr.warning('Valid UID is required'); |
| 1268 | + logSlashCommandWarn('getEntryFieldCallback: Valid UID is required', args, { uid }); | |
| 1269 | + console.warn(); | |
| 1263 | 1270 | return ''; |
| 1264 | 1271 | } |
| 1265 | 1272 | |
| 1266 | 1273 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { |
| 1267 | 1274 | toastr.warning('Valid field name is required'); |
| 1275 | + logSlashCommandWarn('getEntryFieldCallback: Valid field name is required', args, { uid }); | |
| 1268 | 1276 | return ''; |
| 1269 | 1277 | } |
| 1270 | 1278 | |
| @@ -1313,6 +1321,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1313 | 1321 | |
| 1314 | 1322 | if (!data || !('entries' in data)) { |
| 1315 | 1323 | toastr.warning('Valid World Info file name is required'); |
| 1324 | + logSlashCommandWarn('createEntryCallback: Valid World Info file name is required', args); | |
| 1316 | 1325 | return ''; |
| 1317 | 1326 | } |
| 1318 | 1327 | |
| @@ -1358,6 +1367,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1358 | 1367 | |
| 1359 | 1368 | if (value === undefined) { |
| 1360 | 1369 | toastr.warning('Value is required'); |
| 1370 | + logSlashCommandWarn('setEntryFieldCallback: Value is required', args, { value }); | |
| 1361 | 1371 | return ''; |
| 1362 | 1372 | } |
| 1363 | 1373 | |
| @@ -1367,6 +1377,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1367 | 1377 | |
| 1368 | 1378 | if (!data || !('entries' in data)) { |
| 1369 | 1379 | toastr.warning('Valid World Info file name is required'); |
| 1380 | + logSlashCommandWarn('setEntryFieldCallback: Valid World Info file name is required', args, { value }); | |
| 1370 | 1381 | return ''; |
| 1371 | 1382 | } |
| 1372 | 1383 | |
| @@ -1374,11 +1385,13 @@ function registerWorldInfoSlashCommands() { | ||
| 1374 | 1385 | |
| 1375 | 1386 | if (!entry) { |
| 1376 | 1387 | toastr.warning('Valid UID is required'); |
| 1388 | + logSlashCommandWarn('setEntryFieldCallback: Valid UID is required', args, { value }); | |
| 1377 | 1389 | return ''; |
| 1378 | 1390 | } |
| 1379 | 1391 | |
| 1380 | 1392 | if (!Object.hasOwn(newWorldInfoEntryDefinition, field)) { |
| 1381 | 1393 | toastr.warning('Valid field name is required'); |
| 1394 | + logSlashCommandWarn('setEntryFieldCallback: Valid field name is required', args, { value }); | |
| 1382 | 1395 | return ''; |
| 1383 | 1396 | } |
| 1384 | 1397 | |
| @@ -1445,7 +1458,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1445 | 1458 | const uid = value; |
| 1446 | 1459 | const effect = args.effect; |
| 1447 | 1460 | |
| 1448 | 1461 | const entries = await getEntriesFromFile(file, { args, unnamed: { uid }, callbackName: 'getTimedEffectCallback' }); |
| 1449 | 1462 | |
| 1450 | 1463 | if (!entries) { |
| 1451 | 1464 | return ''; |
| @@ -1456,6 +1469,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1456 | 1469 | |
| 1457 | 1470 | if (!entry) { |
| 1458 | 1471 | toastr.warning('Valid UID is required'); |
| 1472 | + logSlashCommandWarn('getTimedEffectCallback: Valid UID is required', args, { uid }); | |
| 1459 | 1473 | return ''; |
| 1460 | 1474 | } |
| 1461 | 1475 | |
| @@ -1465,6 +1479,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1465 | 1479 | |
| 1466 | 1480 | if (!timedEffects.isValidEffectType(effect)) { |
| 1467 | 1481 | toastr.warning('Valid effect type is required'); |
| 1482 | + logSlashCommandWarn('getTimedEffectCallback: Valid effect type is required', args, { uid }); | |
| 1468 | 1483 | return ''; |
| 1469 | 1484 | } |
| 1470 | 1485 | |
| @@ -1488,10 +1503,11 @@ function registerWorldInfoSlashCommands() { | ||
| 1488 | 1503 | |
| 1489 | 1504 | if (value === undefined) { |
| 1490 | 1505 | toastr.warning('New state is required'); |
| 1506 | + logSlashCommandWarn('setTimedEffectCallback: New state is required', args, { value }); | |
| 1491 | 1507 | return ''; |
| 1492 | 1508 | } |
| 1493 | 1509 | |
| 1494 | 1510 | const entries = await getEntriesFromFile(file, { args, unnamed: { value }, callbackName: 'setTimedEffectCallback' }); |
| 1495 | 1511 | |
| 1496 | 1512 | if (!entries) { |
| 1497 | 1513 | return ''; |
| @@ -1502,6 +1518,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1502 | 1518 | |
| 1503 | 1519 | if (!entry) { |
| 1504 | 1520 | toastr.warning('Valid UID is required'); |
| 1521 | + logSlashCommandWarn('setTimedEffectCallback: Valid UID is required', args, { value }); | |
| 1505 | 1522 | return ''; |
| 1506 | 1523 | } |
| 1507 | 1524 | |
| @@ -1511,11 +1528,13 @@ function registerWorldInfoSlashCommands() { | ||
| 1511 | 1528 | |
| 1512 | 1529 | if (!timedEffects.isValidEffectType(effect)) { |
| 1513 | 1530 | toastr.warning('Valid effect type is required'); |
| 1531 | + logSlashCommandWarn('setTimedEffectCallback: Valid effect type is required', args, { value }); | |
| 1514 | 1532 | return ''; |
| 1515 | 1533 | } |
| 1516 | 1534 | |
| 1517 | 1535 | if (!entry[effect]) { |
| 1518 | 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 | 1538 | return ''; |
| 1520 | 1539 | } |
| 1521 | 1540 | |