STscript improvements (see below)

/abort command, {{pipe}} macro in named args, subcommand batch escaping with backslash, string literals and rules for /if, else clause for /if
This commit is contained in:
Cohee
2023-11-24 00:18:07 +02:00
parent 3594c4aac7
commit c50ed4bf6a
2 changed files with 112 additions and 47 deletions

View File

@ -3,6 +3,10 @@ import { extension_settings } from "./extensions.js";
import { executeSlashCommands, registerSlashCommand } from "./slash-commands.js";
function getLocalVariable(name) {
if (!chat_metadata.variables) {
chat_metadata.variables = {};
}
const localVariable = chat_metadata?.variables[name];
return localVariable || '';
@ -125,67 +129,101 @@ function listVariablesCallback() {
}
async function ifCallback(args, command) {
const a = getLocalVariable(args.a) || getGlobalVariable(args.a) || Number(args.a);
const b = getLocalVariable(args.b) || getGlobalVariable(args.b) || Number(args.b);
// Resultion order: numeric literal, local variable, global variable, string literal
const a = isNaN(Number(args.a)) ? (getLocalVariable(args.a) || getGlobalVariable(args.a) || args.a || '') : Number(args.a);
const b = isNaN(Number(args.b)) ? (getLocalVariable(args.b) || getGlobalVariable(args.b) || args.b || '') : Number(args.b);
const rule = args.rule;
if (!a || !b || !rule) {
if (!rule) {
toastr.warning('Both operands and the rule must be specified for the /if command.', 'Invalid /if command');
return '';
}
const aNumber = Number(a);
const bNumber = Number(b);
if (isNaN(aNumber) || isNaN(bNumber)) {
toastr.warning('Both operands must be numbers for the /if command.', 'Invalid /if command');
if ((typeof a === 'number' && isNaN(a)) || (typeof a === 'string' && a === '')) {
toastr.warning('The first operand must be a number, string or a variable name for the /if command.', 'Invalid /if command');
return '';
}
let result = false;
switch (rule) {
case 'gt':
result = aNumber > bNumber;
break;
case 'gte':
result = aNumber >= bNumber;
break;
case 'lt':
result = aNumber < bNumber;
break;
case 'lte':
result = aNumber <= bNumber;
break;
case 'eq':
result = aNumber === bNumber;
break;
default:
toastr.warning('Unknown rule for the /if command.', 'Invalid /if command');
return '';
if (typeof a === 'string') {
const aString = String(a).toLowerCase();
const bString = String(b).toLowerCase();
switch (rule) {
case 'in':
result = aString.includes(bString);
break;
case 'nin':
result = !aString.includes(bString);
break;
case 'eq':
result = aString === bString;
break;
case 'neq':
result = aString !== bString;
break;
default:
toastr.warning('Unknown rule for the /if command for type string.', 'Invalid /if command');
return '';
}
} else if (typeof a === 'number') {
const aNumber = Number(a);
const bNumber = Number(b);
switch (rule) {
case 'gt':
result = aNumber > bNumber;
break;
case 'gte':
result = aNumber >= bNumber;
break;
case 'lt':
result = aNumber < bNumber;
break;
case 'lte':
result = aNumber <= bNumber;
break;
case 'eq':
result = aNumber === bNumber;
break;
case 'neq':
result = aNumber !== bNumber;
break;
default:
toastr.warning('Unknown rule for the /if command for type number.', 'Invalid /if command');
return '';
}
}
if (result && command) {
if (command.startsWith('"')) {
command = command.slice(1);
}
if (command.endsWith('"')) {
command = command.slice(0, -1);
}
const result = await executeSlashCommands(command);
if (!result || typeof result !== 'object') {
return '';
}
return result?.pipe || '';
return await executeSubCommands(command);
} else if (!result && args.else && typeof args.else === 'string' && args.else !== '') {
return await executeSubCommands(args.else);
}
return '';
}
async function executeSubCommands(command) {
if (command.startsWith('"')) {
command = command.slice(1);
}
if (command.endsWith('"')) {
command = command.slice(0, -1);
}
const unescape = true;
const result = await executeSlashCommands(command, unescape);
if (!result || typeof result !== 'object') {
return '';
}
return result?.pipe || '';
}
export function registerVariableCommands() {
registerSlashCommand('listvar', listVariablesCallback, [''], ' list registered chat variables', true, true);
registerSlashCommand('setvar', (args, value) => setLocalVariable(args.key || args.name, value), [], '<span class="monospace">key=varname (value)</span> set a local variable value and pass it down the pipe, e.g. <tt>/setvar key=color green</tt>', true, true);
@ -194,5 +232,5 @@ export function registerVariableCommands() {
registerSlashCommand('setglobalvar', (args, value) => setGlobalVariable(args.key || args.name, value), [], '<span class="monospace">key=varname (value)</span> set a global variable value and pass it down the pipe, e.g. <tt>/setglobalvar key=color green</tt>', true, true);
registerSlashCommand('getglobalvar', (_, value) => getGlobalVariable(value), [], '<span class="monospace">(key)</span> get a global variable value and pass it down the pipe, e.g. <tt>/getglobalvar height</tt>', true, true);
registerSlashCommand('addglobalvar', (args, value) => addGlobalVariable(args.key || args.name, value), [], '<span class="monospace">key=varname (increment)</span> add a value to a global variable and pass the result down the pipe, e.g. <tt>/addglobalvar score 10</tt>', true, true);
registerSlashCommand('if', ifCallback, [], '<span class="monospace">a=varname1 b=varname2 rule=comparison "(command)"</span> compare the value of variable "a" with the value of variable "b", and if the condition yields true, then execute any valid slash command enclosed in quotes and pass the result of the command execution down the pipe. Numeric values for "a" and "b" supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b e.g. <tt>/if a=score a=10 rule=gte "/speak You win"</tt> triggers a /speak command if the value of "score" is greater or equals 10.', true, true);
registerSlashCommand('if', ifCallback, [], '<span class="monospace">a=varname1 b=varname2 rule=comparison else="(alt.command)" "(command)"</span> compare the value of variable "a" with the value of variable "b", and if the condition yields true, then execute any valid slash command enclosed in quotes and pass the result of the command execution down the pipe. Numeric values and string literals for "a" and "b" supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, in (strings) => a includes b, nin (strings) => a not includes b, e.g. <tt>/if a=score a=10 rule=gte "/speak You win"</tt> triggers a /speak command if the value of "score" is greater or equals 10.', true, true);
}