diff --git a/public/scripts/variables.js b/public/scripts/variables.js
index 1eecb3b77..48bcb763d 100644
--- a/public/scripts/variables.js
+++ b/public/scripts/variables.js
@@ -319,6 +319,20 @@ async function whileCallback(args, command) {
return '';
}
+async function timesCallback(args, value) {
+ const [repeats, ...commandParts] = value.split(' ');
+ const command = commandParts.join(' ');
+ const MAX_LOOPS = 100;
+ const isGuardOff = ['off', 'false', '0'].includes(args.guard?.toLowerCase());
+ const iterations = Math.min(Number(repeats), isGuardOff ? Number.MAX_SAFE_INTEGER : MAX_LOOPS);
+
+ for (let i = 0; i < iterations; i++) {
+ await executeSubCommands(command.replace(/\{\{timesIndex\}\}/g, i));
+ }
+
+ return '';
+}
+
async function ifCallback(args, command) {
const { a, b, rule } = parseBooleanOperands(args);
const result = evalBoolean(rule, a, b);
@@ -656,6 +670,7 @@ export function registerVariableCommands() {
registerSlashCommand('decglobalvar', (_, value) => decrementGlobalVariable(value), [], '(key) – decrement a global variable by 1 and pass the result down the pipe, e.g. /decglobalvar score', true, true);
registerSlashCommand('if', ifCallback, [], 'left=varname1 right=varname2 rule=comparison else="(alt.command)" "(command)" – compare the value of the left operand "a" with the value of the right operand "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 left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. /if left=score right=10 rule=gte "/speak You win" triggers a /speak command if the value of "score" is greater or equals 10.', true, true);
registerSlashCommand('while', whileCallback, [], 'left=varname1 right=varname2 rule=comparison "(command)" – compare the value of the left operand "a" with the value of the right operand "b", and if the condition yields true, then execute any valid slash command enclosed in quotes. Numeric values and string literals for left and right operands supported. Available rules: gt => a > b, gte => a >= b, lt => a < b, lte => a <= b, eq => a == b, neq => a != b, not => !a, in (strings) => a includes b, nin (strings) => a not includes b, e.g. /setvar key=i 0 | /while left=i right=10 rule=let "/addvar key=i 1" adds 1 to the value of "i" until it reaches 10. Loops are limited to 100 iterations by default, pass guard=off to disable.', true, true);
+ registerSlashCommand('times', (args, value) => timesCallback(args, value), [], '(repeats) "(command)" – execute any valid slash command enclosed in quotes repeats number of times, e.g. /setvar key=i 1 | /times 5 "/addvar key=i 1" adds 1 to the value of "i" 5 times. {{timesIndex}} is replaced with the iteration number (zero-based), e.g. /times 4 "/echo {{timesIndex}}" echos the numbers 0 through 4. Loops are limited to 100 iterations by default, pass guard=off to disable.', true, true);
registerSlashCommand('flushvar', (_, value) => deleteLocalVariable(value), [], '(key) – delete a local variable, e.g. /flushvar score', true, true);
registerSlashCommand('flushglobalvar', (_, value) => deleteGlobalVariable(value), [], '(key) – delete a global variable, e.g. /flushglobalvar score', true, true);
registerSlashCommand('add', (_, value) => addValuesCallback(value), [], '(a b c d) – performs an addition of the set of values and passes the result down the pipe, can use variable names, e.g. /add 10 i 30 j', true, true);