Compare commits
26 Commits
Author | SHA1 | Date |
---|---|---|
|
83595482b7 | |
|
cdc710385f | |
|
59d4435c32 | |
|
2cfa923093 | |
|
c58ebcc9f9 | |
|
e01097b1e3 | |
|
4efbed7424 | |
|
4a5e33c9a0 | |
|
6bf671f82d | |
|
587d5aa19d | |
|
1bb8c1a308 | |
|
e06abac9cf | |
|
c80b7c2235 | |
|
959fd6e120 | |
|
de02ebcc09 | |
|
f989be56db | |
|
255955d854 | |
|
2f088907ad | |
|
f38096fafb | |
|
67d5704a76 | |
|
3f6162a1a4 | |
|
d715214c01 | |
|
1eac531b41 | |
|
b9c5af6c73 | |
|
b9d436ba92 | |
|
e553042e88 |
10
PRIVACY.md
10
PRIVACY.md
|
@ -29,6 +29,11 @@ Like all Discord bots, _The Bot_ reads every message that it is allowed to, mean
|
||||||
* The Inline Roll System only stores the Discord Guild ID upon usage. Discord Guild IDs are internal IDs generated and provided by Discord.
|
* The Inline Roll System only stores the Discord Guild ID upon usage. Discord Guild IDs are internal IDs generated and provided by Discord.
|
||||||
* _The Bot_ only uses the stored Discord Guild IDs to determine which Guilds it should do a preliminary scan for Inline Rolls for all messages sent.
|
* _The Bot_ only uses the stored Discord Guild IDs to determine which Guilds it should do a preliminary scan for Inline Rolls for all messages sent.
|
||||||
* The Discord Guild IDs are only visible to _The Developer_ thru direct database administration. This direct database administration is only used when there are issues with _The Bot_'s database.
|
* The Discord Guild IDs are only visible to _The Developer_ thru direct database administration. This direct database administration is only used when there are issues with _The Bot_'s database.
|
||||||
|
* The Unrestricted Repeat Roll System (in Discord, these commands are known as `/toggle-unrestricted-repeat enable`, `[[repeat enable`, `[[repeat allow`, `/toggle-unrestricted-repeat disable`, `[[repeat block`, `[[repeat disable`, and `[[repeat delete`):
|
||||||
|
* This system is entirely optional, meaning users never need to run these commands under normal usage of _The Bot_. This system is only intended to be used when a user wants to utilize Unrestricted Repeat Rolls in their Guild.
|
||||||
|
* The Unrestricted Repeat Roll System only stores the Discord Guild ID upon usage. Discord Guild IDs are internal IDs generated and provided by Discord.
|
||||||
|
* _The Bot_ only uses the stored Discord Guild IDs to determine which Guilds it should allow Unrestricted Repeat Rolls in.
|
||||||
|
* The Discord Guild IDs are only visible to _The Developer_ thru direct database administration. This direct database administration is only used when there are issues with _The Bot_'s database.
|
||||||
* The Roll Alias System (in Discord, this system contains all commands starting with `/alias`, `[[rollalias`, `[[ralias`, `[[alias`, `[[rolla`, and `[[ra`, and the subcommands that _The Bot_ will store data from are `add`, `create`, `set`, `update`, `replace`, `copy`, `clone`, and `rename`):
|
* The Roll Alias System (in Discord, this system contains all commands starting with `/alias`, `[[rollalias`, `[[ralias`, `[[alias`, `[[rolla`, and `[[ra`, and the subcommands that _The Bot_ will store data from are `add`, `create`, `set`, `update`, `replace`, `copy`, `clone`, and `rename`):
|
||||||
* This system is entirely optional, meaning users never need to run these commands under normal usage of _The Bot_. This system is only intended to be used when a user wants to save roll commands for later reuse.
|
* This system is entirely optional, meaning users never need to run these commands under normal usage of _The Bot_. This system is only intended to be used when a user wants to save roll commands for later reuse.
|
||||||
* The Roll Alias System stores the user provided Alias Name, the user provided Roll String, and in Guild mode, the Discord Guild ID of the Guild the command was run in; and in personal mode, the Discord User ID of the user who ran the command. The Alias Name is string of up to 200 characters that the user provided to save the Roll String under. The Roll String is a string of up to 4,000 characters containing roll commands and formatting text the user provided. Discord Guild IDs and Discord User IDs are internal IDs generated and provided by Discord.
|
* The Roll Alias System stores the user provided Alias Name, the user provided Roll String, and in Guild mode, the Discord Guild ID of the Guild the command was run in; and in personal mode, the Discord User ID of the user who ran the command. The Alias Name is string of up to 200 characters that the user provided to save the Roll String under. The Roll String is a string of up to 4,000 characters containing roll commands and formatting text the user provided. Discord Guild IDs and Discord User IDs are internal IDs generated and provided by Discord.
|
||||||
|
@ -83,6 +88,11 @@ If you would like to remove your Discord Guild ID from _The Bot_'s database, sim
|
||||||
|
|
||||||
Additionally, _The Bot_ will automatically delete the Discord Guild ID from _The Bot_'s database when _The Bot_ is removed from your guild.
|
Additionally, _The Bot_ will automatically delete the Discord Guild ID from _The Bot_'s database when _The Bot_ is removed from your guild.
|
||||||
|
|
||||||
|
## Unrestricted Repeat Roll System Data Deletion
|
||||||
|
If you would like to remove your Discord Guild ID from _The Bot_'s database, simply send one of the following commands in the Guild: `/toggle-unrestricted-repeat disable`, `[[repeat disable`, `[[repeat block`, or `[[repeat delete`. All variants of this delete the Discord Guild ID from _The Bot_'s database.
|
||||||
|
|
||||||
|
Additionally, _The Bot_ will automatically delete the Discord Guild ID from _The Bot_'s database when _The Bot_ is removed from your guild.
|
||||||
|
|
||||||
## Alias System Data Deletion
|
## Alias System Data Deletion
|
||||||
### Personal Mode
|
### Personal Mode
|
||||||
If you would like to remove all your Personal aliases from _The Bot_'s database, simply send one of the following commands in any channel you share with _The Bot_: `/alias personal delete-all`, `[[alias delete-all`, or `[[alias remove-all`. As deletion is irreversible, _The Bot_ requires a verification code to execute the deletion. _The Bot_ will create a verification code for you when you first send the command.
|
If you would like to remove all your Personal aliases from _The Bot_'s database, simply send one of the following commands in any channel you share with _The Bot_: `/alias personal delete-all`, `[[alias delete-all`, or `[[alias remove-all`. As deletion is irreversible, _The Bot_ requires a verification code to execute the deletion. _The Bot_ will create a verification code for you when you first send the command.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# The Artificer - A Dice Rolling Discord Bot | V4.0.0 - 2025/07/23
|
# The Artificer - A Dice Rolling Discord Bot | V4.1.2 - 2025/08/06
|
||||||
[](https://sonarcloud.io/summary/new_code?id=TheArtificer)
|
[](https://sonarcloud.io/summary/new_code?id=TheArtificer)
|
||||||
[](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer)
|
[](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer) [](https://sonarcloud.io/summary/new_code?id=TheArtificer)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export const config = {
|
export const config = {
|
||||||
name: 'The Artificer', // Name of the bot
|
name: 'The Artificer', // Name of the bot
|
||||||
maxFileSize: 8_388_290, // Max file size bot can send
|
maxFileSize: 8_388_290, // Max file size bot can send
|
||||||
version: '4.0.0', // Version of the bot
|
version: '4.1.2', // Version of the bot
|
||||||
token: 'the_bot_token', // Discord API Token for this bot
|
token: 'the_bot_token', // Discord API Token for this bot
|
||||||
localtoken: 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
|
localtoken: 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
|
||||||
prefix: '[[', // Prefix for all commands
|
prefix: '[[', // Prefix for all commands
|
||||||
|
@ -16,7 +16,7 @@ export const config = {
|
||||||
guild: 1_000, // Allows guilds to have 1000 aliased rolls for free
|
guild: 1_000, // Allows guilds to have 1000 aliased rolls for free
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
maxLoops: 1_000_000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk.
|
maxLoops: 2_000_000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk.
|
||||||
maxWorkers: 16, // Maximum number of worker threads to spawn at once (Set this to less than the number of threads your CPU has, Artificer will eat it all if too many rolls happen at once)
|
maxWorkers: 16, // Maximum number of worker threads to spawn at once (Set this to less than the number of threads your CPU has, Artificer will eat it all if too many rolls happen at once)
|
||||||
workerTimeout: 300_000, // Maximum time before the bot kills a worker thread in ms
|
workerTimeout: 300_000, // Maximum time before the bot kills a worker thread in ms
|
||||||
defaultSimulatedNominal: 10_000, // Default number of loops to run for simulating a nominal
|
defaultSimulatedNominal: 10_000, // Default number of loops to run for simulating a nominal
|
||||||
|
|
|
@ -22,8 +22,20 @@ await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`);
|
||||||
await dbClient.execute(`DROP TABLE IF EXISTS ignore_list;`);
|
await dbClient.execute(`DROP TABLE IF EXISTS ignore_list;`);
|
||||||
await dbClient.execute(`DROP TABLE IF EXISTS allow_inline;`);
|
await dbClient.execute(`DROP TABLE IF EXISTS allow_inline;`);
|
||||||
await dbClient.execute(`DROP TABLE IF EXISTS aliases;`);
|
await dbClient.execute(`DROP TABLE IF EXISTS aliases;`);
|
||||||
|
await dbClient.execute(`DROP TABLE IF EXISTS allow_unrestricted_repeat;`);
|
||||||
console.log('Tables dropped');
|
console.log('Tables dropped');
|
||||||
|
|
||||||
|
// Holds guilds that have explicitly allowed anyone to repeat anyone's rolls
|
||||||
|
console.log('Attempting to create table allow_unrestricted_repeat');
|
||||||
|
await dbClient.execute(`
|
||||||
|
CREATE TABLE allow_unrestricted_repeat (
|
||||||
|
guildid bigint unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (guildid),
|
||||||
|
UNIQUE KEY allow_unrestricted_repeat_guildid_UNIQUE (guildid)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
`);
|
||||||
|
console.log('Table created');
|
||||||
|
|
||||||
// Holds all aliases that have been created
|
// Holds all aliases that have been created
|
||||||
console.log('Attempting to create table aliases');
|
console.log('Attempting to create table aliases');
|
||||||
await dbClient.execute(`
|
await dbClient.execute(`
|
||||||
|
|
|
@ -25,6 +25,7 @@ const commands = [
|
||||||
'ping',
|
'ping',
|
||||||
'privacy',
|
'privacy',
|
||||||
'rip',
|
'rip',
|
||||||
|
'repeat',
|
||||||
'report',
|
'report',
|
||||||
'roll',
|
'roll',
|
||||||
'rolldecorators',
|
'rolldecorators',
|
||||||
|
|
|
@ -60,6 +60,22 @@ This document uses the default prefix (`[[`) on all commands listed. If a comma
|
||||||
* `/toggle-inline-rolls disable`
|
* `/toggle-inline-rolls disable`
|
||||||
* `[[inline block` or `[[inline disable` or `[[inline delete`
|
* `[[inline block` or `[[inline disable` or `[[inline delete`
|
||||||
* Blocks inline rolls in the guild.
|
* Blocks inline rolls in the guild.
|
||||||
|
* `/toggle-unrestricted-repeat [subcommand]` or `[[repeat [subcommand]`
|
||||||
|
* Controls whether or not unrestricted repeat rolls can be done in a guild, defaults off. Unrestricted Repeat Rolls are whether or not anyone in a guild can use the `Repeat Roll` button on anyone's roll or only the original roller can use them. These commands may only be used by the Owner or Admins of your guild.
|
||||||
|
* An inline roll is a roll that does not immediately start with `[[`, such as `test [[d20]]`.
|
||||||
|
* Available subcommands:
|
||||||
|
* `/toggle-unrestricted-repeat help`
|
||||||
|
* `[[repeat help`
|
||||||
|
* Provides a message similar to this subcommand description.
|
||||||
|
* `/toggle-unrestricted-repeat status`
|
||||||
|
* `[[repeat status`
|
||||||
|
* Shows the current status of unrestricted repeat rolls for this guild.
|
||||||
|
* `/toggle-unrestricted-repeat enable`
|
||||||
|
* `[[repeat allow` or `[[repeat enable`
|
||||||
|
* Allows unrestricted repeat rolls in the guild.
|
||||||
|
* `/toggle-unrestricted-repeat disable`
|
||||||
|
* `[[repeat block` or `[[repeat disable` or `[[repeat delete`
|
||||||
|
* Blocks unrestricted repeat rolls in the guild.
|
||||||
* `/alias [subcommand]` or `[[rollalias [subcommand]` or `[[ralias [subcommand]` or `[[alias [subcommand]` or `[[rolla [subcommand]` or `[[ra [subcommand]`
|
* `/alias [subcommand]` or `[[rollalias [subcommand]` or `[[ralias [subcommand]` or `[[alias [subcommand]` or `[[rolla [subcommand]` or `[[ra [subcommand]`
|
||||||
* Custom Roll Alias System
|
* Custom Roll Alias System
|
||||||
* Allows anyone to store a roll string as a shortcut/alias for later use/reuse.
|
* Allows anyone to store a roll string as a shortcut/alias for later use/reuse.
|
||||||
|
|
|
@ -23,6 +23,7 @@ export interface SolvedRoll {
|
||||||
line1: string;
|
line1: string;
|
||||||
line2: string;
|
line2: string;
|
||||||
line3: string;
|
line3: string;
|
||||||
|
footer: string;
|
||||||
counts: CountDetails;
|
counts: CountDetails;
|
||||||
rollDistributions: RollDistributionMap;
|
rollDistributions: RollDistributionMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import { tokenizeCmd } from 'artigen/cmdTokenizer.ts';
|
||||||
|
|
||||||
import { Modifiers } from 'artigen/dice/getModifiers.ts';
|
import { Modifiers } from 'artigen/dice/getModifiers.ts';
|
||||||
|
|
||||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
||||||
|
|
||||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
import { reduceCountDetails } from 'artigen/utils/counter.ts';
|
||||||
import { cmdSplitRegex, escapeCharacters, withYVarsDash } from 'artigen/utils/escape.ts';
|
import { cmdSplitRegex, escapeCharacters, withYVarsDash } from 'artigen/utils/escape.ts';
|
||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
import { loggingEnabled, showLoopCountDebug } from 'artigen/utils/logFlag.ts';
|
||||||
import { assertPrePostBalance } from 'artigen/utils/parenBalance.ts';
|
import { assertPrePostBalance } from 'artigen/utils/parenBalance.ts';
|
||||||
import { reduceRollDistMaps } from 'artigen/utils/rollDist.ts';
|
import { reduceRollDistMaps } from 'artigen/utils/rollDist.ts';
|
||||||
import { compareTotalRolls, compareTotalRollsReverse, sortYVars } from 'artigen/utils/sortFuncs.ts';
|
import { compareTotalRolls, compareTotalRollsReverse, sortYVars } from 'artigen/utils/sortFuncs.ts';
|
||||||
|
@ -26,6 +26,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
line1: '',
|
line1: '',
|
||||||
line2: '',
|
line2: '',
|
||||||
line3: '',
|
line3: '',
|
||||||
|
footer: '',
|
||||||
counts: {
|
counts: {
|
||||||
total: 0,
|
total: 0,
|
||||||
successful: 0,
|
successful: 0,
|
||||||
|
@ -33,6 +34,9 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
rerolled: 0,
|
rerolled: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
exploded: 0,
|
exploded: 0,
|
||||||
|
success: 0,
|
||||||
|
fail: 0,
|
||||||
|
matches: new Map<string, number>(),
|
||||||
},
|
},
|
||||||
rollDistributions: new Map<string, number[]>(),
|
rollDistributions: new Map<string, number[]>(),
|
||||||
};
|
};
|
||||||
|
@ -103,10 +107,10 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List number of iterations on simulated nominals
|
// List number of iterations on simulated nominals
|
||||||
if (rollRequest.modifiers.simulatedNominal) line2 += `Iterations performed per roll: \`${rollRequest.modifiers.simulatedNominal}\`\n`;
|
if (rollRequest.modifiers.simulatedNominal) line2 += `Iterations performed per roll: \`${rollRequest.modifiers.simulatedNominal.toLocaleString()}\`\n`;
|
||||||
|
|
||||||
// Reduce counts to a single object
|
// Reduce counts to a single object
|
||||||
returnMsg.counts = reduceCountDetails(tempCountDetails);
|
if (rollRequest.modifiers.count) returnMsg.counts = reduceCountDetails(tempCountDetails);
|
||||||
|
|
||||||
// If a regular nominal and roll looks somewhat complex, alert user simulatedNominal exists
|
// If a regular nominal and roll looks somewhat complex, alert user simulatedNominal exists
|
||||||
if (rollRequest.modifiers.nominalRoll && tempReturnData.filter((data) => data.isComplex).length) {
|
if (rollRequest.modifiers.nominalRoll && tempReturnData.filter((data) => data.isComplex).length) {
|
||||||
|
@ -117,7 +121,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
const line2Space = rollRequest.modifiers.noSpaces ? '' : ' ';
|
const line2Space = rollRequest.modifiers.noSpaces ? '' : ' ';
|
||||||
// Fill out all of the details and results now
|
// Fill out all of the details and results now
|
||||||
tempReturnData.forEach((e, i) => {
|
tempReturnData.forEach((e, i) => {
|
||||||
loopCountCheck();
|
loopCountCheck('artigen.ts - tempReturnData');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Parsing roll ${rollRequest.rollCmd} | Making return text ${JSON.stringify(e)}`);
|
loggingEnabled && log(LT.LOG, `Parsing roll ${rollRequest.rollCmd} | Making return text ${JSON.stringify(e)}`);
|
||||||
let preFormat = '';
|
let preFormat = '';
|
||||||
|
@ -166,7 +170,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
returnMsg.line3 = line3;
|
returnMsg.line3 = line3;
|
||||||
|
|
||||||
// Reduce rollDist maps into a single map
|
// Reduce rollDist maps into a single map
|
||||||
returnMsg.rollDistributions = reduceRollDistMaps(tempRollDists);
|
if (rollRequest.modifiers.rollDist) returnMsg.rollDistributions = reduceRollDistMaps(tempRollDists);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Fill in the return block
|
// Fill in the return block
|
||||||
const solverError = e as Error;
|
const solverError = e as Error;
|
||||||
|
@ -175,5 +179,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
|
||||||
[returnMsg.errorCode, returnMsg.errorMsg] = translateError(solverError);
|
[returnMsg.errorCode, returnMsg.errorMsg] = translateError(solverError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showLoopCountDebug) returnMsg.footer = `Loop Count: ${getLoopCount()}`;
|
||||||
|
|
||||||
return returnMsg;
|
return returnMsg;
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,7 +32,7 @@ export const tokenizeCmd = (
|
||||||
|
|
||||||
// Wrapped commands still exist, unwrap them
|
// Wrapped commands still exist, unwrap them
|
||||||
while (cmd.includes(config.prefix)) {
|
while (cmd.includes(config.prefix)) {
|
||||||
loopCountCheck();
|
loopCountCheck('cmdTokenizer.ts - while cmd includes prefix');
|
||||||
|
|
||||||
const openIdx = cmd.indexOf(config.prefix);
|
const openIdx = cmd.indexOf(config.prefix);
|
||||||
const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
|
const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
|
||||||
|
@ -49,7 +49,7 @@ export const tokenizeCmd = (
|
||||||
|
|
||||||
const simulatedData: ReturnData[] = [];
|
const simulatedData: ReturnData[] = [];
|
||||||
for (let i = 0; i < simulatedLoopCount; i++) {
|
for (let i = 0; i < simulatedLoopCount; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck(`cmdTokenizer.ts - simulate nominal loop #${i}`);
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `In simLoop:${i} "${currentCmd}" of ${JSON.stringify(cmd)}`);
|
loggingEnabled && log(LT.LOG, `In simLoop:${i} "${currentCmd}" of ${JSON.stringify(cmd)}`);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ export const tokenizeCmd = (
|
||||||
loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)}`);
|
loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)}`);
|
||||||
let done = false;
|
let done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
loopCountCheck();
|
loopCountCheck('cmdTokenizer.ts - confirming crit');
|
||||||
|
|
||||||
// Keep running the same roll again until its not successful
|
// Keep running the same roll again until its not successful
|
||||||
const [ccTempData, ccTempCounts, ccTempDists] = tokenizeCmd(
|
const [ccTempData, ccTempCounts, ccTempDists] = tokenizeCmd(
|
||||||
|
@ -194,7 +194,7 @@ export const tokenizeCmd = (
|
||||||
const tempInitConf = data.initConfig.split(internalGrpWrapRegex).filter((x) => x);
|
const tempInitConf = data.initConfig.split(internalGrpWrapRegex).filter((x) => x);
|
||||||
loggingEnabled && log(LT.LOG, `Split solved math into tempInitConf ${JSON.stringify(tempInitConf)}`);
|
loggingEnabled && log(LT.LOG, `Split solved math into tempInitConf ${JSON.stringify(tempInitConf)}`);
|
||||||
while (tempInitConf.includes(openInternalGrp)) {
|
while (tempInitConf.includes(openInternalGrp)) {
|
||||||
loopCountCheck();
|
loopCountCheck('cmdTokenizer.ts - handling internal group result merging');
|
||||||
|
|
||||||
const openIdx = tempInitConf.indexOf(openInternalGrp);
|
const openIdx = tempInitConf.indexOf(openInternalGrp);
|
||||||
const closeIdx = getMatchingInternalGrpIdx(tempInitConf, openIdx);
|
const closeIdx = getMatchingInternalGrpIdx(tempInitConf, openIdx);
|
||||||
|
@ -214,7 +214,7 @@ export const tokenizeCmd = (
|
||||||
.filter((x) => x);
|
.filter((x) => x);
|
||||||
loggingEnabled && log(LT.LOG, `Split tempInitConfig into initConf ${JSON.stringify(initConf)}`);
|
loggingEnabled && log(LT.LOG, `Split tempInitConfig into initConf ${JSON.stringify(initConf)}`);
|
||||||
while (initConf.includes(openInternal)) {
|
while (initConf.includes(openInternal)) {
|
||||||
loopCountCheck();
|
loopCountCheck('cmdTokenizer.ts - handling internal nested roll result merging');
|
||||||
|
|
||||||
const openIdx = initConf.indexOf(openInternal);
|
const openIdx = initConf.indexOf(openInternal);
|
||||||
const closeIdx = getMatchingInternalIdx(initConf, openIdx);
|
const closeIdx = getMatchingInternalIdx(initConf, openIdx);
|
||||||
|
|
|
@ -29,6 +29,9 @@ export interface CountDetails {
|
||||||
rerolled: number;
|
rerolled: number;
|
||||||
dropped: number;
|
dropped: number;
|
||||||
exploded: number;
|
exploded: number;
|
||||||
|
success: number;
|
||||||
|
fail: number;
|
||||||
|
matches: Map<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RollDistribution is used for storing the raw roll distribution
|
// RollDistribution is used for storing the raw roll distribution
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
for (let i = 0; i < rollConf.dieCount; i++) {
|
for (let i = 0; i < rollConf.dieCount; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - handling initial rolling');
|
||||||
|
|
||||||
// Copy the template to fill out for this iteration
|
// Copy the template to fill out for this iteration
|
||||||
const rolling = getTemplateRoll();
|
const rolling = getTemplateRoll();
|
||||||
|
@ -104,7 +104,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
for (let i = 0; i < rollSet.length; i++) {
|
for (let i = 0; i < rollSet.length; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - handling rerolling and exploding');
|
||||||
|
|
||||||
// This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll
|
// This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll
|
||||||
if (rollConf.reroll.on && rollConf.reroll.nums.includes(rollSet[i].roll) && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) {
|
if (rollConf.reroll.on && rollConf.reroll.nums.includes(rollSet[i].roll) && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) {
|
||||||
|
@ -117,7 +117,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
if (modifiers.maxRoll && !minMaxOverride) {
|
if (modifiers.maxRoll && !minMaxOverride) {
|
||||||
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
|
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
|
||||||
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
|
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - maximizeRoll');
|
||||||
|
|
||||||
if (!rollConf.reroll.nums.includes(m)) {
|
if (!rollConf.reroll.nums.includes(m)) {
|
||||||
minMaxOverride = m;
|
minMaxOverride = m;
|
||||||
|
@ -127,7 +127,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
} else if (modifiers.minRoll && !minMaxOverride) {
|
} else if (modifiers.minRoll && !minMaxOverride) {
|
||||||
// If minimizeRoll is on and we've entered the reroll code, 1 is not allowed, determine the next best option and always return that
|
// If minimizeRoll is on and we've entered the reroll code, 1 is not allowed, determine the next best option and always return that
|
||||||
mmMinLoop: for (let m = rollConf.dPercent.on ? 1 : 2; m <= rollConf.dieSize; m++) {
|
mmMinLoop: for (let m = rollConf.dPercent.on ? 1 : 2; m <= rollConf.dieSize; m++) {
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - minimizeRoll');
|
||||||
|
|
||||||
if (!rollConf.reroll.nums.includes(m)) {
|
if (!rollConf.reroll.nums.includes(m)) {
|
||||||
minMaxOverride = m;
|
minMaxOverride = m;
|
||||||
|
@ -181,7 +181,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
for (const penRoll of rollSet) {
|
for (const penRoll of rollSet) {
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - penetrating explosion');
|
||||||
|
|
||||||
// If the die was from an explosion, decrement it by one
|
// If the die was from an explosion, decrement it by one
|
||||||
if (penRoll.exploding) {
|
if (penRoll.exploding) {
|
||||||
|
@ -195,7 +195,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
for (let i = 0; i < rollSet.length; i++) {
|
for (let i = 0; i < rollSet.length; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - compounding explosion');
|
||||||
|
|
||||||
// Compound the exploding rolls, including the exploding flag and
|
// Compound the exploding rolls, including the exploding flag and
|
||||||
if (rollSet[i].exploding) {
|
if (rollSet[i].exploding) {
|
||||||
|
@ -215,8 +215,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
let rerollCount = 0;
|
let rerollCount = 0;
|
||||||
if (rollConf.reroll.on) {
|
if (rollConf.reroll.on) {
|
||||||
for (let j = 0; j < rollSet.length; j++) {
|
for (let j = 0; j < rollSet.length; j++) {
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
loopCountCheck('executeRoll.ts - count rerolls');
|
||||||
loopCountCheck();
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
||||||
rollSet[j].origIdx = j;
|
rollSet[j].origIdx = j;
|
||||||
|
@ -265,8 +264,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
// Now its time to drop all dice needed
|
// Now its time to drop all dice needed
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (dropCount > 0 && i < rollSet.length) {
|
while (dropCount > 0 && i < rollSet.length) {
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
loopCountCheck('executeRoll.ts - dropping/keeping');
|
||||||
loopCountCheck();
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
||||||
// Skip all rolls that were rerolled
|
// Skip all rolls that were rerolled
|
||||||
|
@ -290,7 +288,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
|
|
||||||
// Drop all dice that are not a part of the max
|
// Drop all dice that are not a part of the max
|
||||||
for (const ovaRoll of rollSet) {
|
for (const ovaRoll of rollSet) {
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - OVA');
|
||||||
|
|
||||||
loggingEnabled &&
|
loggingEnabled &&
|
||||||
log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`);
|
log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`);
|
||||||
|
@ -311,7 +309,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
let labelIdx = 0;
|
let labelIdx = 0;
|
||||||
const rollLabels: Array<string> = rollVals.map((count) => {
|
const rollLabels: Array<string> = rollVals.map((count) => {
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - matching');
|
||||||
|
|
||||||
if (labelIdx >= labels.length) {
|
if (labelIdx >= labels.length) {
|
||||||
throw new Error(`TooManyLabels_${labels.length}`);
|
throw new Error(`TooManyLabels_${labels.length}`);
|
||||||
|
@ -327,7 +325,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
|
||||||
|
|
||||||
// Apply labels
|
// Apply labels
|
||||||
for (const roll of rollSet) {
|
for (const roll of rollSet) {
|
||||||
loopCountCheck();
|
loopCountCheck('executeRoll.ts - labeling matches');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | trying to add a label to ${JSON.stringify(roll)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | trying to add a label to ${JSON.stringify(roll)}`);
|
||||||
if (rollLabels[roll.roll - 1]) {
|
if (rollLabels[roll.roll - 1]) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const formatRoll = (executedRoll: ExecutedRoll, modifiers: RollModifiers)
|
||||||
// Loop thru all parts of the roll to document everything that was done to create the total roll
|
// Loop thru all parts of the roll to document everything that was done to create the total roll
|
||||||
loggingEnabled && log(LT.LOG, `Formatting roll ${JSON.stringify(executedRoll)}`);
|
loggingEnabled && log(LT.LOG, `Formatting roll ${JSON.stringify(executedRoll)}`);
|
||||||
executedRoll.rollSet.forEach((e) => {
|
executedRoll.rollSet.forEach((e) => {
|
||||||
loopCountCheck();
|
loopCountCheck('generateFormattedRoll.ts - formatting executed roll');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `At ${JSON.stringify(e)}`);
|
loggingEnabled && log(LT.LOG, `At ${JSON.stringify(e)}`);
|
||||||
let preFormat = '';
|
let preFormat = '';
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const getGroupConf = (groupStr: string, rawStr: string): GroupConf => {
|
||||||
|
|
||||||
let biggest = parseInt(numberMatches.length ? numberMatches[0] : '1');
|
let biggest = parseInt(numberMatches.length ? numberMatches[0] : '1');
|
||||||
for (const num of numberMatches) {
|
for (const num of numberMatches) {
|
||||||
loopCountCheck();
|
loopCountCheck('getGroupConf.ts - finding biggest number for die size');
|
||||||
|
|
||||||
const curNum = parseInt(num);
|
const curNum = parseInt(num);
|
||||||
loggingEnabled && log(LT.LOG, `Finding biggest number to use as die size, ${curNum} ${biggest}`);
|
loggingEnabled && log(LT.LOG, `Finding biggest number to use as die size, ${curNum} ${biggest}`);
|
||||||
|
@ -37,7 +37,7 @@ export const getGroupConf = (groupStr: string, rawStr: string): GroupConf => {
|
||||||
let maxFail: number | null = null;
|
let maxFail: number | null = null;
|
||||||
|
|
||||||
while (groupSplit.length) {
|
while (groupSplit.length) {
|
||||||
loopCountCheck();
|
loopCountCheck('getGroupConf.ts - parsing groupConf');
|
||||||
|
|
||||||
const option = groupSplit.shift() ?? '';
|
const option = groupSplit.shift() ?? '';
|
||||||
const value = parseInt(groupSplit.shift() ?? '');
|
const value = parseInt(groupSplit.shift() ?? '');
|
||||||
|
|
|
@ -247,5 +247,11 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
||||||
modifiers.valid = false;
|
modifiers.valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (modifiers.simulatedNominal < 0) {
|
||||||
|
modifiers.error.name = 'NegativeSimNominal';
|
||||||
|
modifiers.error.message = 'Number of iterations for `simulatedNominal` must be at least 1';
|
||||||
|
modifiers.valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
return [modifiers, args];
|
return [modifiers, args];
|
||||||
};
|
};
|
||||||
|
|
|
@ -126,7 +126,7 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
|
||||||
const difficulty = parseInt(tempDifficulty.slice(0, afterDifficultyIdx) || '10');
|
const difficulty = parseInt(tempDifficulty.slice(0, afterDifficultyIdx) || '10');
|
||||||
|
|
||||||
for (let i = difficulty; i <= rollConf.dieSize; i++) {
|
for (let i = difficulty; i <= rollConf.dieSize; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck('getRollConf.ts - setting cwod difficulty');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
|
||||||
rollConf.success.range.push(i);
|
rollConf.success.range.push(i);
|
||||||
|
@ -216,7 +216,7 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
|
||||||
|
|
||||||
// Loop until all remaining args are parsed
|
// Loop until all remaining args are parsed
|
||||||
while (remains.length > 0) {
|
while (remains.length > 0) {
|
||||||
loopCountCheck();
|
loopCountCheck('getRollConf.ts - parsing rollConf');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing remains ${remains}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing remains ${remains}`);
|
||||||
// Find the next number in the remains to be able to cut out the rule name
|
// Find the next number in the remains to be able to cut out the rule name
|
||||||
|
@ -227,9 +227,13 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
|
||||||
|
|
||||||
// Determine if afterSepIdx needs to be moved up (cases like mt! or !mt)
|
// Determine if afterSepIdx needs to be moved up (cases like mt! or !mt)
|
||||||
const tempSep = remains.slice(0, afterSepIdx);
|
const tempSep = remains.slice(0, afterSepIdx);
|
||||||
|
loggingEnabled && log(LT.LOG, `tempSep: ${tempSep}`);
|
||||||
|
|
||||||
let noNumberAfter = false;
|
let noNumberAfter = false;
|
||||||
|
if (!(Object.values(DiceOptions) as string[]).includes(tempSep)) {
|
||||||
NumberlessDiceOptions.some((opt) => {
|
NumberlessDiceOptions.some((opt) => {
|
||||||
loopCountCheck();
|
loopCountCheck('getRollConf.ts - parsing numberlessDiceOptions');
|
||||||
|
loggingEnabled && log(LT.LOG, `In NumberlessDiceOptions ${opt} ${tempSep.startsWith(opt) && tempSep !== opt}`);
|
||||||
if (tempSep.startsWith(opt) && tempSep !== opt) {
|
if (tempSep.startsWith(opt) && tempSep !== opt) {
|
||||||
afterSepIdx = opt.length;
|
afterSepIdx = opt.length;
|
||||||
noNumberAfter = true;
|
noNumberAfter = true;
|
||||||
|
@ -237,10 +241,13 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
|
||||||
}
|
}
|
||||||
return tempSep === opt;
|
return tempSep === opt;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Save the rule name to tSep and remove it from remains
|
// Save the rule name to tSep and remove it from remains
|
||||||
const tSep = remains.slice(0, afterSepIdx);
|
const tSep = remains.slice(0, afterSepIdx);
|
||||||
remains = remains.slice(afterSepIdx);
|
remains = remains.slice(afterSepIdx);
|
||||||
|
loggingEnabled && log(LT.LOG, `tSep: ${tSep}, remains: ${remains}`);
|
||||||
|
|
||||||
// Find the next non-number in the remains to be able to cut out the count/num
|
// Find the next non-number in the remains to be able to cut out the count/num
|
||||||
let afterNumIdx = noNumberAfter ? 0 : remains.search(/(?![-\d])/);
|
let afterNumIdx = noNumberAfter ? 0 : remains.search(/(?![-\d])/);
|
||||||
if (afterNumIdx < 0) {
|
if (afterNumIdx < 0) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const handleGroup = (
|
||||||
|
|
||||||
// Nested groups still exist, unwrap them
|
// Nested groups still exist, unwrap them
|
||||||
while (groupParts.includes('{')) {
|
while (groupParts.includes('{')) {
|
||||||
loopCountCheck();
|
loopCountCheck('groupHandler.ts - handling nested groups');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Handling Nested Groups | Current cmd: ${JSON.stringify(groupParts)}`);
|
loggingEnabled && log(LT.LOG, `Handling Nested Groups | Current cmd: ${JSON.stringify(groupParts)}`);
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ export const handleGroup = (
|
||||||
const groupResults: ReturnData[] = [];
|
const groupResults: ReturnData[] = [];
|
||||||
|
|
||||||
for (const part of commaParts) {
|
for (const part of commaParts) {
|
||||||
loopCountCheck();
|
loopCountCheck('groupHandler.ts - solving commaParts');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Solving commaPart: ${part}`);
|
loggingEnabled && log(LT.LOG, `Solving commaPart: ${part}`);
|
||||||
const [tempData, tempCounts, tempDists] = tokenizeMath(part, modifiers, previousResults, prevGrpReturnData);
|
const [tempData, tempCounts, tempDists] = tokenizeMath(part, modifiers, previousResults, prevGrpReturnData);
|
||||||
|
@ -131,7 +131,7 @@ export const handleGroup = (
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (dropCount > 0 && i < groupResults.length) {
|
while (dropCount > 0 && i < groupResults.length) {
|
||||||
loopCountCheck();
|
loopCountCheck('groupHandler.ts - handling group drop/keep');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}`);
|
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}`);
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ export const handleGroup = (
|
||||||
let failCnt = 0;
|
let failCnt = 0;
|
||||||
if (groupConf.success.on || groupConf.fail.on) {
|
if (groupConf.success.on || groupConf.fail.on) {
|
||||||
groupResults.forEach((rd, idx) => {
|
groupResults.forEach((rd, idx) => {
|
||||||
loopCountCheck();
|
loopCountCheck('groupHandler.ts - handling group success/fail');
|
||||||
|
|
||||||
if (!resultFlags[idx].dropped) {
|
if (!resultFlags[idx].dropped) {
|
||||||
if (
|
if (
|
||||||
|
@ -265,7 +265,7 @@ export const handleGroup = (
|
||||||
const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x);
|
const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x);
|
||||||
loggingEnabled && log(LT.LOG, `Split retData into initConf ${JSON.stringify(initConf)}`);
|
loggingEnabled && log(LT.LOG, `Split retData into initConf ${JSON.stringify(initConf)}`);
|
||||||
while (initConf.includes(openInternalGrp)) {
|
while (initConf.includes(openInternalGrp)) {
|
||||||
loopCountCheck();
|
loopCountCheck('groupHandler.ts - handling merging nested groups up');
|
||||||
|
|
||||||
const openIdx = initConf.indexOf(openInternalGrp);
|
const openIdx = initConf.indexOf(openInternalGrp);
|
||||||
const closeIdx = getMatchingInternalGrpIdx(initConf, openIdx);
|
const closeIdx = getMatchingInternalGrpIdx(initConf, openIdx);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { closeLog, initLog } from '@Log4Deno';
|
import { closeLog, initLog } from '@Log4Deno';
|
||||||
|
|
||||||
import { runCmd } from 'artigen/artigen.ts';
|
import { runCmd } from 'artigen/artigen.ts';
|
||||||
|
import { SolvedRoll } from 'artigen/artigen.d.ts';
|
||||||
|
|
||||||
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
|
||||||
|
|
||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
import { loggingEnabled, loopLoggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
|
|
||||||
loggingEnabled && initLog('logs/worker', loggingEnabled);
|
if (loggingEnabled || loopLoggingEnabled) initLog('logs/worker', loggingEnabled || loopLoggingEnabled);
|
||||||
|
|
||||||
// Extend the BigInt prototype to support JSON.stringify
|
// Extend the BigInt prototype to support JSON.stringify
|
||||||
interface BigIntX extends BigInt {
|
interface BigIntX extends BigInt {
|
||||||
|
@ -23,13 +24,14 @@ self.postMessage('ready');
|
||||||
// Handle the roll
|
// Handle the roll
|
||||||
self.onmessage = async (e: MessageEvent<QueuedRoll>) => {
|
self.onmessage = async (e: MessageEvent<QueuedRoll>) => {
|
||||||
const payload = e.data;
|
const payload = e.data;
|
||||||
const returnMsg = runCmd(payload) || {
|
const returnMsg: SolvedRoll = runCmd(payload) || {
|
||||||
error: true,
|
error: true,
|
||||||
errorCode: 'EmptyMessage',
|
errorCode: 'EmptyMessage',
|
||||||
errorMsg: 'Error: Empty message',
|
errorMsg: 'Error: Empty message',
|
||||||
line1: '',
|
line1: '',
|
||||||
line2: '',
|
line2: '',
|
||||||
line3: '',
|
line3: '',
|
||||||
|
footer: '',
|
||||||
counts: {
|
counts: {
|
||||||
total: 0,
|
total: 0,
|
||||||
successful: 0,
|
successful: 0,
|
||||||
|
@ -37,9 +39,12 @@ self.onmessage = async (e: MessageEvent<QueuedRoll>) => {
|
||||||
rerolled: 0,
|
rerolled: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
exploded: 0,
|
exploded: 0,
|
||||||
|
success: 0,
|
||||||
|
fail: 0,
|
||||||
|
matches: new Map<string, number>(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.postMessage(returnMsg);
|
self.postMessage(returnMsg);
|
||||||
loggingEnabled && (await closeLog());
|
if (loggingEnabled || loopLoggingEnabled) await closeLog();
|
||||||
self.close();
|
self.close();
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,15 @@ import utils from 'utils/utils.ts';
|
||||||
import { STATUS_CODE, STATUS_TEXT } from '@std/http/status';
|
import { STATUS_CODE, STATUS_TEXT } from '@std/http/status';
|
||||||
|
|
||||||
const getUserIdForEmbed = (rollRequest: QueuedRoll): bigint => {
|
const getUserIdForEmbed = (rollRequest: QueuedRoll): bigint => {
|
||||||
|
if (rollRequest.apiRoll) return rollRequest.api.userId;
|
||||||
|
if (rollRequest.ddRoll) {
|
||||||
|
if (rollRequest.dd.overrideAuthorId === 0n) return rollRequest.dd.authorId;
|
||||||
|
return rollRequest.dd.overrideAuthorId;
|
||||||
|
}
|
||||||
|
return 0n;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAuthorIdForButton = (rollRequest: QueuedRoll): bigint => {
|
||||||
if (rollRequest.apiRoll) return rollRequest.api.userId;
|
if (rollRequest.apiRoll) return rollRequest.api.userId;
|
||||||
if (rollRequest.ddRoll) return rollRequest.dd.authorId;
|
if (rollRequest.ddRoll) return rollRequest.dd.authorId;
|
||||||
return 0n;
|
return 0n;
|
||||||
|
@ -189,6 +198,7 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
newMsg = await rollRequest.dd.myResponse.edit({
|
newMsg = await rollRequest.dd.myResponse.edit({
|
||||||
|
content: rollRequest.dd.overrideAuthorId === 0n ? '' : `<@${rollRequest.dd.overrideAuthorId}> used the \`Repeat Roll\` button for the referenced message:`,
|
||||||
embeds: pubEmbeds,
|
embeds: pubEmbeds,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
|
@ -197,7 +207,7 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
||||||
{
|
{
|
||||||
type: MessageComponentTypes.Button,
|
type: MessageComponentTypes.Button,
|
||||||
label: 'Repeat Roll',
|
label: 'Repeat Roll',
|
||||||
customId: `${repeatRollCustomId}${InteractionValueSeparator}${getUserIdForEmbed(rollRequest).toString()}`,
|
customId: `${repeatRollCustomId}${InteractionValueSeparator}${getAuthorIdForButton(rollRequest).toString()}`,
|
||||||
style: ButtonStyles.Secondary,
|
style: ButtonStyles.Secondary,
|
||||||
emoji: '🎲',
|
emoji: '🎲',
|
||||||
},
|
},
|
||||||
|
@ -268,7 +278,7 @@ Please click on "<@${botId}> *Click to see attachment*" above this message to se
|
||||||
details: returnMsg.line3,
|
details: returnMsg.line3,
|
||||||
},
|
},
|
||||||
counts: rollRequest.modifiers.count ? returnMsg.counts : null,
|
counts: rollRequest.modifiers.count ? returnMsg.counts : null,
|
||||||
rollDistributions: returnMsg.rollDistributions.entries().toArray(),
|
rollDistributions: rollRequest.modifiers.rollDist ? returnMsg.rollDistributions.entries().toArray() : null,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
@ -286,7 +296,7 @@ Please click on "<@${botId}> *Click to see attachment*" above this message to se
|
||||||
embeds: [
|
embeds: [
|
||||||
(
|
(
|
||||||
await generateRollEmbed(
|
await generateRollEmbed(
|
||||||
rollRequest.dd.authorId,
|
0n,
|
||||||
<SolvedRoll> {
|
<SolvedRoll> {
|
||||||
error: true,
|
error: true,
|
||||||
errorMsg:
|
errorMsg:
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const terminateWorker = async (rollWorker: Worker, rollRequest: QueuedRol
|
||||||
embeds: [
|
embeds: [
|
||||||
(
|
(
|
||||||
await generateRollEmbed(
|
await generateRollEmbed(
|
||||||
rollRequest.dd.authorId,
|
0n,
|
||||||
<SolvedRoll> {
|
<SolvedRoll> {
|
||||||
error: true,
|
error: true,
|
||||||
errorCode: 'TooComplex',
|
errorCode: 'TooComplex',
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
|
import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
import config from '~config';
|
import config from '~config';
|
||||||
|
|
||||||
|
import { loopLoggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
|
|
||||||
let loopCount = 0;
|
let loopCount = 0;
|
||||||
|
|
||||||
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
|
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
|
||||||
export const loopCountCheck = (): void => {
|
export const loopCountCheck = (location = 'unset'): void => {
|
||||||
loopCount++;
|
loopCount++;
|
||||||
|
loopLoggingEnabled && log(LT.LOG, `Loop #${loopCount} at "${location}"`);
|
||||||
if (loopCount > config.limits.maxLoops) {
|
if (loopCount > config.limits.maxLoops) {
|
||||||
throw new Error('MaxLoopsExceeded');
|
throw new Error('MaxLoopsExceeded');
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ interface DDQueuedRoll extends BaseQueuedRoll {
|
||||||
dd: {
|
dd: {
|
||||||
myResponse: DiscordenoMessage;
|
myResponse: DiscordenoMessage;
|
||||||
originalMessage: DiscordenoMessage;
|
originalMessage: DiscordenoMessage;
|
||||||
|
overrideAuthorId: bigint;
|
||||||
authorId: bigint;
|
authorId: bigint;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
|
||||||
|
|
||||||
// Evaluate all parenthesis
|
// Evaluate all parenthesis
|
||||||
while (conf.includes('(')) {
|
while (conf.includes('(')) {
|
||||||
loopCountCheck();
|
loopCountCheck('mathSolver.ts - evaluating parens');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
||||||
// Get first open parenthesis
|
// Get first open parenthesis
|
||||||
|
@ -75,7 +75,7 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
|
||||||
// Start at index 1 as there will never be implicit multiplication before the first element
|
// Start at index 1 as there will never be implicit multiplication before the first element
|
||||||
loggingEnabled && log(LT.LOG, `Checking for missing implicit multiplication ${JSON.stringify(conf)}`);
|
loggingEnabled && log(LT.LOG, `Checking for missing implicit multiplication ${JSON.stringify(conf)}`);
|
||||||
for (let i = 1; i < conf.length; i++) {
|
for (let i = 1; i < conf.length; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck('mathSolver.ts - checking for implicit multiplication');
|
||||||
|
|
||||||
const prevConfAsStr = <string> conf[i - 1];
|
const prevConfAsStr = <string> conf[i - 1];
|
||||||
const curConfAsStr = <string> conf[i];
|
const curConfAsStr = <string> conf[i];
|
||||||
|
@ -94,14 +94,15 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
|
||||||
['+', '-'],
|
['+', '-'],
|
||||||
];
|
];
|
||||||
allCurOps.forEach((curOps) => {
|
allCurOps.forEach((curOps) => {
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
|
// No loopCountCheck here since its finite/will always be 3 loops
|
||||||
|
loggingEnabled && log(LT.LOG, `Cur Ops ${JSON.stringify(curOps)}`);
|
||||||
// Iterate thru all operators/operands in the conf
|
// Iterate thru all operators/operands in the conf
|
||||||
for (let i = 0; i < conf.length; i++) {
|
for (let i = 0; i < conf.length; i++) {
|
||||||
loopCountCheck();
|
loggingEnabled && log(LT.LOG, `Checking ${JSON.stringify(conf[i])}`);
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
|
|
||||||
// Check if the current index is in the active tier of operators
|
// Check if the current index is in the active tier of operators
|
||||||
if (curOps.includes(conf[i].toString())) {
|
if (curOps.includes(conf[i].toString())) {
|
||||||
|
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} as ${JSON.stringify(conf[i])} is in curOps`);
|
||||||
|
loopCountCheck('mathSolver.ts - evaluating roll');
|
||||||
// Grab the operands from before and after the operator
|
// Grab the operands from before and after the operator
|
||||||
const operand1 = conf[i - 1];
|
const operand1 = conf[i - 1];
|
||||||
const operand2 = conf[i + 1];
|
const operand2 = conf[i + 1];
|
||||||
|
|
|
@ -45,11 +45,11 @@ export const tokenizeMath = (
|
||||||
loggingEnabled && log(LT.LOG, `Split roll into mathConf ${JSON.stringify(mathConf)}`);
|
loggingEnabled && log(LT.LOG, `Split roll into mathConf ${JSON.stringify(mathConf)}`);
|
||||||
|
|
||||||
// Verify balanced parens before doing anything
|
// Verify balanced parens before doing anything
|
||||||
assertParenBalance(mathConf);
|
if (mathConf.includes('(') || mathConf.includes(')')) assertParenBalance(mathConf);
|
||||||
|
|
||||||
// Evaluate all rolls into stepSolve format and all numbers into floats
|
// Evaluate all rolls into stepSolve format and all numbers into floats
|
||||||
for (let i = 0; i < mathConf.length; i++) {
|
for (let i = 0; i < mathConf.length; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck('mathTokenizer.ts - parsing all tokens into MathConf');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
|
loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ export const tokenizeMath = (
|
||||||
const formattedRoll = formatRoll(executedRoll, modifiers);
|
const formattedRoll = formatRoll(executedRoll, modifiers);
|
||||||
mathConf[i] = formattedRoll.solvedStep;
|
mathConf[i] = formattedRoll.solvedStep;
|
||||||
countDetails.push(formattedRoll.countDetails);
|
countDetails.push(formattedRoll.countDetails);
|
||||||
rollDists.push(formattedRoll.rollDistributions);
|
if (modifiers.rollDist) rollDists.push(formattedRoll.rollDistributions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ export const tokenizeMath = (
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (dropCount > 0 && i < allRollSets.length) {
|
while (dropCount > 0 && i < allRollSets.length) {
|
||||||
loopCountCheck();
|
loopCountCheck('mathTokenizer.ts - handling group dropping');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}, looking at ${JSON.stringify(allRollSets[i])}`);
|
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}, looking at ${JSON.stringify(allRollSets[i])}`);
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ export const tokenizeMath = (
|
||||||
// Handle marking new successes/fails
|
// Handle marking new successes/fails
|
||||||
if (groupConf.success.on || groupConf.fail.on) {
|
if (groupConf.success.on || groupConf.fail.on) {
|
||||||
allRollSets.forEach((rs) => {
|
allRollSets.forEach((rs) => {
|
||||||
loopCountCheck();
|
loopCountCheck('mathTokenizer.ts - handling group success/fails');
|
||||||
|
|
||||||
if (!rs.dropped && !rs.rerolled) {
|
if (!rs.dropped && !rs.rerolled) {
|
||||||
if (groupConf.success.on && groupConf.success.range.includes(rs.roll)) {
|
if (groupConf.success.on && groupConf.success.range.includes(rs.roll)) {
|
||||||
|
@ -318,7 +318,7 @@ export const tokenizeMath = (
|
||||||
const formattedRoll = formatRoll(executedRoll, modifiers);
|
const formattedRoll = formatRoll(executedRoll, modifiers);
|
||||||
mathConf[rollGroupIdx] = formattedRoll.solvedStep;
|
mathConf[rollGroupIdx] = formattedRoll.solvedStep;
|
||||||
countDetails.push(formattedRoll.countDetails);
|
countDetails.push(formattedRoll.countDetails);
|
||||||
rollDists.push(formattedRoll.rollDistributions);
|
if (modifiers.rollDist) rollDists.push(formattedRoll.rollDistributions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,29 @@ import { CountDetails, RollSet } from 'artigen/dice/dice.d.ts';
|
||||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
export const rollCounter = (rollSet: RollSet[]): CountDetails => {
|
export const rollCounter = (rollSet: RollSet[]): CountDetails => {
|
||||||
const countDetails = {
|
const countDetails: CountDetails = {
|
||||||
total: 0,
|
total: 0,
|
||||||
successful: 0,
|
successful: 0,
|
||||||
failed: 0,
|
failed: 0,
|
||||||
rerolled: 0,
|
rerolled: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
exploded: 0,
|
exploded: 0,
|
||||||
|
success: 0,
|
||||||
|
fail: 0,
|
||||||
|
matches: new Map<string, number>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
rollSet.forEach((roll) => {
|
rollSet.forEach((roll) => {
|
||||||
loopCountCheck();
|
loopCountCheck('counter.ts - summing RollSet into CountDetails');
|
||||||
countDetails.total++;
|
countDetails.total++;
|
||||||
if (roll.critHit) countDetails.successful++;
|
if (roll.critHit) countDetails.successful++;
|
||||||
if (roll.critFail) countDetails.failed++;
|
if (roll.critFail) countDetails.failed++;
|
||||||
if (roll.rerolled) countDetails.rerolled++;
|
if (roll.rerolled) countDetails.rerolled++;
|
||||||
if (roll.dropped) countDetails.dropped++;
|
if (roll.dropped) countDetails.dropped++;
|
||||||
if (roll.exploding) countDetails.exploded++;
|
if (roll.exploding) countDetails.exploded++;
|
||||||
|
if (roll.success) countDetails.success++;
|
||||||
|
if (roll.fail) countDetails.fail++;
|
||||||
|
if (roll.matchLabel) countDetails.matches.set(roll.matchLabel, (countDetails.matches.get(roll.matchLabel) ?? 0) + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
return countDetails;
|
return countDetails;
|
||||||
|
@ -28,7 +34,11 @@ export const rollCounter = (rollSet: RollSet[]): CountDetails => {
|
||||||
export const reduceCountDetails = (counts: CountDetails[]): CountDetails =>
|
export const reduceCountDetails = (counts: CountDetails[]): CountDetails =>
|
||||||
counts.reduce(
|
counts.reduce(
|
||||||
(acc, cur) => {
|
(acc, cur) => {
|
||||||
loopCountCheck();
|
loopCountCheck('counter.ts - merging array of CountDetails down to single CountDetail');
|
||||||
|
cur.matches.forEach((cnt, label) => {
|
||||||
|
loopCountCheck('counter.ts - merging matches');
|
||||||
|
acc.matches.set(label, (acc.matches.get(label) ?? 0) + cnt);
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
total: acc.total + cur.total,
|
total: acc.total + cur.total,
|
||||||
successful: acc.successful + cur.successful,
|
successful: acc.successful + cur.successful,
|
||||||
|
@ -36,6 +46,9 @@ export const reduceCountDetails = (counts: CountDetails[]): CountDetails =>
|
||||||
rerolled: acc.rerolled + cur.rerolled,
|
rerolled: acc.rerolled + cur.rerolled,
|
||||||
dropped: acc.dropped + cur.dropped,
|
dropped: acc.dropped + cur.dropped,
|
||||||
exploded: acc.exploded + cur.exploded,
|
exploded: acc.exploded + cur.exploded,
|
||||||
|
success: acc.success + cur.success,
|
||||||
|
fail: acc.fail + cur.fail,
|
||||||
|
matches: acc.matches,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -45,5 +58,8 @@ export const reduceCountDetails = (counts: CountDetails[]): CountDetails =>
|
||||||
rerolled: 0,
|
rerolled: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
exploded: 0,
|
exploded: 0,
|
||||||
|
success: 0,
|
||||||
|
fail: 0,
|
||||||
|
matches: new Map<string, number>(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -62,12 +62,12 @@ export const generateCountDetailsEmbed = (counts: CountDetails): ArtigenEmbedNoA
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Successful Rolls:',
|
name: 'Critically Successful Rolls:',
|
||||||
value: `${counts.successful}`,
|
value: `${counts.successful}`,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Failed Rolls:',
|
name: 'Critically Failed Rolls:',
|
||||||
value: `${counts.failed}`,
|
value: `${counts.failed}`,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
|
@ -86,6 +86,28 @@ export const generateCountDetailsEmbed = (counts: CountDetails): ArtigenEmbedNoA
|
||||||
value: `${counts.exploded}`,
|
value: `${counts.exploded}`,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Successful Rolls:',
|
||||||
|
value: `${counts.success}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Failed Rolls:',
|
||||||
|
value: `${counts.fail}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Matched Roll Labels:',
|
||||||
|
value: `${
|
||||||
|
counts.matches
|
||||||
|
.entries()
|
||||||
|
.toArray()
|
||||||
|
.sort()
|
||||||
|
.map(([label, count]) => `${label}: ${count}`)
|
||||||
|
.join(', ')
|
||||||
|
}`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -237,13 +259,17 @@ export const generateRollEmbed = (
|
||||||
|
|
||||||
// Embed desc limit is 4096
|
// Embed desc limit is 4096
|
||||||
// Discord only formats 200 items per message
|
// Discord only formats 200 items per message
|
||||||
if (fullDesc.length < 4_000 && formattingCount <= 200) {
|
const fullSize = fullDesc.length + returnDetails.footer.length;
|
||||||
|
if (fullSize < 4_000 && formattingCount <= 200) {
|
||||||
// Response is valid size
|
// Response is valid size
|
||||||
return {
|
return {
|
||||||
charCount: fullDesc.length,
|
charCount: fullSize,
|
||||||
embed: {
|
embed: {
|
||||||
color: infoColor2,
|
color: infoColor2,
|
||||||
description: fullDesc,
|
description: fullDesc,
|
||||||
|
footer: {
|
||||||
|
text: returnDetails.footer,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hasAttachment: false,
|
hasAttachment: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,13 +11,15 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
export const escapeCharacters = (str: string, esc: string): string => {
|
export const escapeCharacters = (str: string, esc: string): string => {
|
||||||
// Loop thru each esc char one at a time
|
// Loop thru each esc char one at a time
|
||||||
for (const e of esc) {
|
for (const e of esc) {
|
||||||
loopCountCheck();
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
||||||
|
if (str.includes(e)) {
|
||||||
|
loopCountCheck(`escape.ts - escaping character ${e}`);
|
||||||
|
|
||||||
// Create a new regex to look for that char that needs replaced and escape it
|
// Create a new regex to look for that char that needs replaced and escape it
|
||||||
const tempRgx = new RegExp(`[${e}]`, 'g');
|
const tempRgx = new RegExp(`[${e}]`, 'g');
|
||||||
str = str.replace(tempRgx, `\\${e}`);
|
str = str.replace(tempRgx, `\\${e}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export const loggingEnabled = false;
|
export const loggingEnabled = false;
|
||||||
|
export const loopLoggingEnabled = false;
|
||||||
|
export const showLoopCountDebug = false;
|
||||||
|
|
|
@ -22,7 +22,8 @@ const checkBalance = (
|
||||||
|
|
||||||
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
|
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
|
||||||
for (let i = openIdx; i < conf.length; i++) {
|
for (let i = openIdx; i < conf.length; i++) {
|
||||||
countLoops && loopCountCheck();
|
countLoops &&
|
||||||
|
loopCountCheck(`parenBalance.ts - ${getMatching ? 'Looking for matching' : 'Checking'} ${openStr}/${closeStr}${getMatching ? '' : ' balance'}`);
|
||||||
loggingEnabled &&
|
loggingEnabled &&
|
||||||
log(
|
log(
|
||||||
LT.LOG,
|
LT.LOG,
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const addToRange = (tSep: string, range: Array<number>, tNum: number) =>
|
||||||
|
|
||||||
const internalAddMultipleToRange = (tSep: string, range: Array<number>, start: number, end: number) => {
|
const internalAddMultipleToRange = (tSep: string, range: Array<number>, start: number, end: number) => {
|
||||||
for (let i = start; i <= end; i++) {
|
for (let i = start; i <= end; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck(`rangeAdder.ts - ${tSep} range adder`);
|
||||||
addToRange(tSep, range, i);
|
addToRange(tSep, range, i);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const createRollDistMap = (rollSet: RollSet[]): RollDistributionMap => {
|
||||||
const rollDistMap = new Map<string, number[]>();
|
const rollDistMap = new Map<string, number[]>();
|
||||||
|
|
||||||
rollSet.forEach((roll) => {
|
rollSet.forEach((roll) => {
|
||||||
loopCountCheck();
|
loopCountCheck('rollDist.ts - convert RollSet into RollDist');
|
||||||
const tempArr: number[] = rollDistMap.get(rollDistKey(roll.type, roll.size)) ?? new Array<number>(roll.type === 'fate' ? roll.size + 2 : roll.size).fill(0);
|
const tempArr: number[] = rollDistMap.get(rollDistKey(roll.type, roll.size)) ?? new Array<number>(roll.type === 'fate' ? roll.size + 2 : roll.size).fill(0);
|
||||||
tempArr[roll.type === 'fate' ? roll.roll + 1 : roll.roll - 1]++;
|
tempArr[roll.type === 'fate' ? roll.roll + 1 : roll.roll - 1]++;
|
||||||
rollDistMap.set(rollDistKey(roll.type, roll.size), tempArr);
|
rollDistMap.set(rollDistKey(roll.type, roll.size), tempArr);
|
||||||
|
@ -22,17 +22,17 @@ export const createRollDistMap = (rollSet: RollSet[]): RollDistributionMap => {
|
||||||
// Collapses an array of RollDistMaps into a single RollDistMap
|
// Collapses an array of RollDistMaps into a single RollDistMap
|
||||||
export const reduceRollDistMaps = (rollDistArr: RollDistributionMap[]): RollDistributionMap =>
|
export const reduceRollDistMaps = (rollDistArr: RollDistributionMap[]): RollDistributionMap =>
|
||||||
rollDistArr.reduce((acc, cur) => {
|
rollDistArr.reduce((acc, cur) => {
|
||||||
loopCountCheck();
|
loopCountCheck('rollDist.ts - merge array of RollDists into single RollDist');
|
||||||
|
|
||||||
cur
|
cur
|
||||||
.entries()
|
.entries()
|
||||||
.toArray()
|
.toArray()
|
||||||
.forEach(([key, value]) => {
|
.forEach(([key, value]) => {
|
||||||
loopCountCheck();
|
loopCountCheck('rollDist.ts - doing the merge on each item of current');
|
||||||
|
|
||||||
const tempArr = acc.get(key) ?? new Array<number>(value.length).fill(0);
|
const tempArr = acc.get(key) ?? new Array<number>(value.length).fill(0);
|
||||||
for (let i = 0; i < tempArr.length; i++) {
|
for (let i = 0; i < tempArr.length; i++) {
|
||||||
loopCountCheck();
|
loopCountCheck('rollDist.ts - doing the merge');
|
||||||
tempArr[i] += value[i];
|
tempArr[i] += value[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export const generateRollVals = (rollConf: RollConf, rollSet: RollSet[], rollStr
|
||||||
|
|
||||||
// Count up all rolls
|
// Count up all rolls
|
||||||
for (const ovaRoll of rollSet) {
|
for (const ovaRoll of rollSet) {
|
||||||
loopCountCheck();
|
loopCountCheck('rollValCounter.ts - counting roll vals');
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
|
||||||
if (!ovaRoll.dropped && !ovaRoll.rerolled) {
|
if (!ovaRoll.dropped && !ovaRoll.rerolled) {
|
||||||
|
|
|
@ -18,10 +18,11 @@ import { roll, rollSC } from 'commands/roll.ts';
|
||||||
import { rollHelp } from 'commands/rollHelp.ts';
|
import { rollHelp } from 'commands/rollHelp.ts';
|
||||||
import { stats, statsSC } from 'commands/stats.ts';
|
import { stats, statsSC } from 'commands/stats.ts';
|
||||||
import { toggleInline, toggleInlineSC } from 'commands/toggleInline.ts';
|
import { toggleInline, toggleInlineSC } from 'commands/toggleInline.ts';
|
||||||
|
import { toggleRepeat, toggleRepeatSC } from 'commands/toggleUnrestrictedRepeat.ts';
|
||||||
import { version, versionSC } from 'commands/version.ts';
|
import { version, versionSC } from 'commands/version.ts';
|
||||||
|
|
||||||
export const announceSlashCommands = () => {
|
export const announceSlashCommands = () => {
|
||||||
upsertSlashCommands([aliasSC, heatmapSC, helpSC, infoSC, privacySC, reportSC, ripSC, rollSC, statsSC, toggleInlineSC, versionSC]);
|
upsertSlashCommands([aliasSC, heatmapSC, helpSC, infoSC, privacySC, reportSC, ripSC, rollSC, statsSC, toggleInlineSC, toggleRepeatSC, versionSC]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const commands = {
|
export const commands = {
|
||||||
|
@ -43,6 +44,7 @@ export const commands = {
|
||||||
rollHelp,
|
rollHelp,
|
||||||
stats,
|
stats,
|
||||||
toggleInline,
|
toggleInline,
|
||||||
|
toggleRepeat,
|
||||||
version,
|
version,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,5 +59,6 @@ export const slashCommandDetails = {
|
||||||
rollSC,
|
rollSC,
|
||||||
statsSC,
|
statsSC,
|
||||||
toggleInlineSC,
|
toggleInlineSC,
|
||||||
|
toggleRepeatSC,
|
||||||
versionSC,
|
versionSC,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { RollAliasHelpPages } from 'commands/helpLibrary/aliasHelp.ts';
|
||||||
import { ApiHelpPages } from 'commands/helpLibrary/apiHelp.ts';
|
import { ApiHelpPages } from 'commands/helpLibrary/apiHelp.ts';
|
||||||
import { HelpContents, HelpPage } from 'commands/helpLibrary/helpLibrary.d.ts';
|
import { HelpContents, HelpPage } from 'commands/helpLibrary/helpLibrary.d.ts';
|
||||||
import { InlineHelpPages } from 'commands/helpLibrary/inlineHelp.ts';
|
import { InlineHelpPages } from 'commands/helpLibrary/inlineHelp.ts';
|
||||||
|
import { RepeatHelpPages } from 'commands/helpLibrary/repeatHelp.ts';
|
||||||
|
|
||||||
import { RootRollHelpPages } from 'commands/helpLibrary/rollHelp/_rootRollHelp.ts';
|
import { RootRollHelpPages } from 'commands/helpLibrary/rollHelp/_rootRollHelp.ts';
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ const dict = new Map<string, HelpPage | HelpContents>([
|
||||||
['roll-help', RootRollHelpPages],
|
['roll-help', RootRollHelpPages],
|
||||||
['alias', RollAliasHelpPages],
|
['alias', RollAliasHelpPages],
|
||||||
['inline', InlineHelpPages],
|
['inline', InlineHelpPages],
|
||||||
|
['repeat', RepeatHelpPages],
|
||||||
[
|
[
|
||||||
'opt-out',
|
'opt-out',
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import config from '~config';
|
||||||
|
|
||||||
|
import { HelpContents, HelpPage } from 'commands/helpLibrary/helpLibrary.d.ts';
|
||||||
|
|
||||||
|
const name = 'Unrestricted Repeat';
|
||||||
|
const description = `${config.name} has an option to allow anyone to use the \`Repeat Roll\` button.
|
||||||
|
|
||||||
|
By default, Unrestricted Repeat Rolls are disabled in your guild, meaning only the original roller can use the \`Repeat Roll\` button. These commands may only be used by the Owner or Admins of your guild.`;
|
||||||
|
const dict = new Map<string, HelpContents>([
|
||||||
|
[
|
||||||
|
'status',
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
description: `**Usage:** \`${config.prefix}repeat status\`
|
||||||
|
|
||||||
|
Shows the current status of Repeat Rolling for this guild.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'enable',
|
||||||
|
{
|
||||||
|
name: 'Allow Unrestricted Repeat',
|
||||||
|
description: `**Usage:** \`${config.prefix}repeat allow\` or \`${config.prefix}repeat enable\`
|
||||||
|
|
||||||
|
Allows Unrestricted Repeat Rolls for this guild. This allows anyone in the guild to use the \`Repeat Roll\` button on any roll from anyone.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'disable',
|
||||||
|
{
|
||||||
|
name: 'Block Unrestricted Repeat',
|
||||||
|
description: `**Usage:** \`${config.prefix}repeat block\` or \`${config.prefix}repeat disable\` or \`${config.prefix}repeat delete\`
|
||||||
|
|
||||||
|
Blocks Unrestricted Repeat rolls for this guild. This only allows the original roller to use the \`Repeat Roll\` button.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'help',
|
||||||
|
{
|
||||||
|
name: 'Help',
|
||||||
|
description: `**Usage:** \`${config.prefix}repeat help\` or \`${config.prefix}repeat h\`
|
||||||
|
|
||||||
|
Opens the help library to the Unrestricted Repeat Help section.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const RepeatHelpPages: HelpPage = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
isPage: true,
|
||||||
|
dict,
|
||||||
|
};
|
|
@ -108,7 +108,7 @@ Rolls the requested roll and orders the results in the requested direction.`,
|
||||||
name: 'Count Rolls',
|
name: 'Count Rolls',
|
||||||
description: `**Usage:** \`-c\`
|
description: `**Usage:** \`-c\`
|
||||||
|
|
||||||
Shows the Count Embed, containing the count of successful rolls, failed rolls, rerolls, drops, and explosions.`,
|
Shows the Count Embed, containing the count of critically successful rolls, critically failed rolls, rerolls, drops, explosions, successes, fails, and matches.`,
|
||||||
example: ['`[[40d20]] -c`'],
|
example: ['`[[40d20]] -c`'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,7 +27,13 @@ export const rollSC: CreateGlobalApplicationCommand = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const roll = async (msgOrInt: DiscordenoMessage | Interaction, args: string[], command: string) => {
|
export const roll = async (
|
||||||
|
msgOrInt: DiscordenoMessage | Interaction,
|
||||||
|
args: string[],
|
||||||
|
command: string,
|
||||||
|
overrideAuthor?: bigint,
|
||||||
|
forceOriginalAuthor?: bigint,
|
||||||
|
) => {
|
||||||
// Light telemetry to see how many times a command is being run
|
// Light telemetry to see how many times a command is being run
|
||||||
const currDateTime = new Date();
|
const currDateTime = new Date();
|
||||||
dbClient.execute(queries.callIncCnt('roll')).catch((e) => utils.commonLoggers.dbError('roll.ts:20', 'call sproc INC_CNT on', e));
|
dbClient.execute(queries.callIncCnt('roll')).catch((e) => utils.commonLoggers.dbError('roll.ts:20', 'call sproc INC_CNT on', e));
|
||||||
|
@ -44,7 +50,7 @@ export const roll = async (msgOrInt: DiscordenoMessage | Interaction, args: stri
|
||||||
originalCommand = `${config.prefix}${originalCommand.trim()}`;
|
originalCommand = `${config.prefix}${originalCommand.trim()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const m = await utils.sendOrInteract(msgOrInt, 'roll.ts:47', rollingEmbed, true);
|
const m = await utils.sendOrInteract(msgOrInt, 'roll.ts:47', rollingEmbed, true, !overrideAuthor);
|
||||||
if (!m) {
|
if (!m) {
|
||||||
throw new Error("My message didn't send!");
|
throw new Error("My message didn't send!");
|
||||||
}
|
}
|
||||||
|
@ -55,6 +61,7 @@ export const roll = async (msgOrInt: DiscordenoMessage | Interaction, args: stri
|
||||||
// Return early if the modifiers were invalid
|
// Return early if the modifiers were invalid
|
||||||
if (!modifiers.valid) {
|
if (!modifiers.valid) {
|
||||||
m.edit(generateRollError('Modifiers invalid:', modifiers.error.name, modifiers.error.message)).catch((e) => utils.commonLoggers.messageEditError('roll.ts:50', m, e));
|
m.edit(generateRollError('Modifiers invalid:', modifiers.error.name, modifiers.error.message)).catch((e) => utils.commonLoggers.messageEditError('roll.ts:50', m, e));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rollCmd = (hasOwnProperty(msgOrInt, 'token') ? args.join('') : msgOrInt.content).startsWith(`${config.prefix}r`) ? remainingArgs.join('') : `${command}${remainingArgs.join('')}`;
|
let rollCmd = (hasOwnProperty(msgOrInt, 'token') ? args.join('') : msgOrInt.content).startsWith(`${config.prefix}r`) ? remainingArgs.join('') : `${command}${remainingArgs.join('')}`;
|
||||||
|
@ -72,7 +79,8 @@ export const roll = async (msgOrInt: DiscordenoMessage | Interaction, args: stri
|
||||||
ddRoll: true,
|
ddRoll: true,
|
||||||
testRoll: false,
|
testRoll: false,
|
||||||
dd: {
|
dd: {
|
||||||
authorId: utils.getAuthorIdFromMessageOrInteraction(msgOrInt),
|
overrideAuthorId: overrideAuthor ?? 0n,
|
||||||
|
authorId: forceOriginalAuthor ? forceOriginalAuthor : utils.getAuthorIdFromMessageOrInteraction(msgOrInt),
|
||||||
myResponse: m,
|
myResponse: m,
|
||||||
originalMessage: hasOwnProperty(msgOrInt, 'token') ? m : msgOrInt,
|
originalMessage: hasOwnProperty(msgOrInt, 'token') ? m : msgOrInt,
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,8 +42,8 @@ export const toggleInline = async (msgOrInt: DiscordenoMessage | Interaction, ar
|
||||||
// Light telemetry to see how many times a command is being run
|
// Light telemetry to see how many times a command is being run
|
||||||
dbClient.execute(queries.callIncCnt('inline')).catch((e) => utils.commonLoggers.dbError('toggleInline.ts:16', 'call sproc INC_CNT on', e));
|
dbClient.execute(queries.callIncCnt('inline')).catch((e) => utils.commonLoggers.dbError('toggleInline.ts:16', 'call sproc INC_CNT on', e));
|
||||||
|
|
||||||
// Local apiArg in lowercase
|
// Local inlineArg in lowercase
|
||||||
const apiArg = (args[0] || '').toLowerCase();
|
const inlineArg = (args[0] || '').toLowerCase();
|
||||||
|
|
||||||
const guildId = BigInt(msgOrInt.guildId ?? '0');
|
const guildId = BigInt(msgOrInt.guildId ?? '0');
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ export const toggleInline = async (msgOrInt: DiscordenoMessage | Interaction, ar
|
||||||
|
|
||||||
if (await hasGuildPermissions(guildId, utils.getAuthorIdFromMessageOrInteraction(msgOrInt), ['ADMINISTRATOR'])) {
|
if (await hasGuildPermissions(guildId, utils.getAuthorIdFromMessageOrInteraction(msgOrInt), ['ADMINISTRATOR'])) {
|
||||||
let enable = false;
|
let enable = false;
|
||||||
switch (apiArg) {
|
switch (inlineArg) {
|
||||||
case 'allow':
|
case 'allow':
|
||||||
case 'enable':
|
case 'enable':
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { CreateGlobalApplicationCommand, DiscordApplicationCommandOptionTypes, DiscordenoMessage, hasGuildPermissions, Interaction } from '@discordeno';
|
||||||
|
|
||||||
|
import config from '~config';
|
||||||
|
|
||||||
|
import { generateHelpMessage } from 'commands/helpLibrary/generateHelpMessage.ts';
|
||||||
|
|
||||||
|
import dbClient from 'db/client.ts';
|
||||||
|
import { queries, repeatList } from 'db/common.ts';
|
||||||
|
|
||||||
|
import { failColor, infoColor1, successColor } from 'embeds/colors.ts';
|
||||||
|
|
||||||
|
import utils from 'utils/utils.ts';
|
||||||
|
|
||||||
|
export const toggleRepeatSC: CreateGlobalApplicationCommand = {
|
||||||
|
name: 'toggle-unrestricted-repeat',
|
||||||
|
description: 'Enable or disable unrestricted repeat rolling for this guild.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
type: DiscordApplicationCommandOptionTypes.SubCommand,
|
||||||
|
name: 'enable',
|
||||||
|
description: 'Enables/Allows unrestricted repeat rolling in this guild.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: DiscordApplicationCommandOptionTypes.SubCommand,
|
||||||
|
name: 'disable',
|
||||||
|
description: 'Disables/Blocks unrestricted repeat rolling in this guild.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: DiscordApplicationCommandOptionTypes.SubCommand,
|
||||||
|
name: 'status',
|
||||||
|
description: 'Gets the current status of repeat rolling for this guild.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: DiscordApplicationCommandOptionTypes.SubCommand,
|
||||||
|
name: 'help',
|
||||||
|
description: 'Opens the help library to the Toggle Repeat help page.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toggleRepeat = async (msgOrInt: DiscordenoMessage | Interaction, args: string[]) => {
|
||||||
|
// Light telemetry to see how many times a command is being run
|
||||||
|
dbClient.execute(queries.callIncCnt('repeat')).catch((e) => utils.commonLoggers.dbError('toggleRepeat.ts:16', 'call sproc INC_CNT on', e));
|
||||||
|
|
||||||
|
// Local repeatArg in lowercase
|
||||||
|
const repeatArg = (args[0] || '').toLowerCase();
|
||||||
|
|
||||||
|
const guildId = BigInt(msgOrInt.guildId ?? '0');
|
||||||
|
|
||||||
|
// Alert users who DM the bot that this command is for guilds only
|
||||||
|
if (guildId === 0n) {
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:45', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: failColor,
|
||||||
|
title: 'Toggle Repeat commands are only available in guilds.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let errorOut = false;
|
||||||
|
const guildQuery = await dbClient.query(`SELECT guildid FROM allow_unrestricted_repeat WHERE guildid = ?`, [guildId]).catch((e0) => {
|
||||||
|
utils.commonLoggers.dbError('toggleRepeat.ts:36', 'query', e0);
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:59', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: failColor,
|
||||||
|
title: 'Failed to check Unrestricted Repeat status for this guild.',
|
||||||
|
description: 'If this issue persists, please report this to the developers.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
errorOut = true;
|
||||||
|
});
|
||||||
|
if (errorOut) return;
|
||||||
|
|
||||||
|
if (await hasGuildPermissions(guildId, utils.getAuthorIdFromMessageOrInteraction(msgOrInt), ['ADMINISTRATOR'])) {
|
||||||
|
let enable = false;
|
||||||
|
switch (repeatArg) {
|
||||||
|
case 'allow':
|
||||||
|
case 'enable':
|
||||||
|
enable = true;
|
||||||
|
if (!repeatList.includes(guildId)) {
|
||||||
|
await dbClient.execute('INSERT INTO allow_unrestricted_repeat(guildid) values(?)', [guildId]).catch((e) => {
|
||||||
|
utils.commonLoggers.dbError('toggleRepeat.ts:58', 'insert into allow_unrestricted_repeat', e);
|
||||||
|
errorOut = true;
|
||||||
|
});
|
||||||
|
if (!errorOut) {
|
||||||
|
repeatList.push(guildId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'block':
|
||||||
|
case 'disable':
|
||||||
|
case 'delete':
|
||||||
|
await dbClient.execute('DELETE FROM allow_unrestricted_repeat WHERE guildid = ?', [guildId]).catch((e) => {
|
||||||
|
utils.commonLoggers.dbError('toggleRepeat.ts:65', 'delete from allow_unrestricted_repeat', e);
|
||||||
|
errorOut = true;
|
||||||
|
});
|
||||||
|
if (!errorOut && repeatList.includes(guildId)) {
|
||||||
|
repeatList.splice(repeatList.indexOf(guildId), 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'status':
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:98', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: infoColor1,
|
||||||
|
title: `Unrestricted Repeat is ${guildQuery.length ? 'Enabled' : 'Disabled'} for this guild`,
|
||||||
|
description: `This means ${guildQuery.length ? 'anyone' : 'only the original roller'} can use the \`Repeat Roll\` button.
|
||||||
|
|
||||||
|
To ${guildQuery.length ? 'disable' : 'enable'} it, run the following command:\n\`${config.prefix}repeat ${guildQuery.length ? 'disable' : 'enable'}\``,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case 'h':
|
||||||
|
case 'help':
|
||||||
|
default:
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:113', generateHelpMessage('repeat'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (errorOut) {
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:117', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: failColor,
|
||||||
|
title: `Failed to ${enable ? 'Enable' : 'Disable'} Unrestricted Repeat for this guild`,
|
||||||
|
description: 'Please try the command again. If this issue persists, please report this to the developers.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:128', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: successColor,
|
||||||
|
title: `Successfully ${enable ? 'Enabled' : 'Disabled'} Unrestricted Repeat for this guild`,
|
||||||
|
description: `${enable ? 'Anyone' : 'Only the original roller'} may now use the \`Repeat Roll\` button.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
utils.sendOrInteract(msgOrInt, 'toggleRepeat.ts:137', {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
color: failColor,
|
||||||
|
title: 'Toggle Unrestricted Repeat commands are powerful and can only be used by guild Owners and Admins.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -21,6 +21,13 @@ dbInlineList.forEach((guildIdObj: GuildIdObj) => {
|
||||||
inlineList.push(guildIdObj.guildid);
|
inlineList.push(guildIdObj.guildid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// List of guilds who have allowed unrestricted repeat rolls
|
||||||
|
export const repeatList: Array<bigint> = [];
|
||||||
|
const dbRepeatList = await dbClient.query('SELECT * FROM allow_unrestricted_repeat');
|
||||||
|
dbRepeatList.forEach((guildIdObj: GuildIdObj) => {
|
||||||
|
repeatList.push(guildIdObj.guildid);
|
||||||
|
});
|
||||||
|
|
||||||
export const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
export const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
||||||
|
|
||||||
export const queries = {
|
export const queries = {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const apiStats = async (): Promise<Response> => {
|
||||||
const memberCount = cache.guilds
|
const memberCount = cache.guilds
|
||||||
.array()
|
.array()
|
||||||
.map((guild) => guild.memberCount)
|
.map((guild) => guild.memberCount)
|
||||||
.reduce(basicReducer);
|
.reduce(basicReducer, 0);
|
||||||
|
|
||||||
const cachedGuilds = await cacheHandlers.size('guilds');
|
const cachedGuilds = await cacheHandlers.size('guilds');
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export const apiStats = async (): Promise<Response> => {
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
guildCount: cachedGuilds + cache.dispatchedGuildIds.size,
|
guildCount: cachedGuilds + cache.dispatchedGuildIds.size,
|
||||||
memberCount,
|
memberCount,
|
||||||
rollCount,
|
rollCount: Number(rollCount),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
status: STATUS_CODE.OK,
|
status: STATUS_CODE.OK,
|
||||||
|
|
|
@ -42,7 +42,10 @@ export const guildDeleteHandler = (guild: DiscordenoGuild) => {
|
||||||
.execute('DELETE FROM allowed_guilds WHERE guildid = ? AND banned = 0', [guild.id])
|
.execute('DELETE FROM allowed_guilds WHERE guildid = ? AND banned = 0', [guild.id])
|
||||||
.catch((e) => utils.commonLoggers.dbError('guildDelete.ts:41', 'delete from', e));
|
.catch((e) => utils.commonLoggers.dbError('guildDelete.ts:41', 'delete from', e));
|
||||||
dbClient.execute('DELETE FROM allow_inline WHERE guildid = ?', [guild.id]).catch((e) => utils.commonLoggers.dbError('guildDelete.ts:42', 'delete from', e));
|
dbClient.execute('DELETE FROM allow_inline WHERE guildid = ?', [guild.id]).catch((e) => utils.commonLoggers.dbError('guildDelete.ts:42', 'delete from', e));
|
||||||
|
dbClient
|
||||||
|
.execute('DELETE FROM allow_unrestricted_repeat WHERE guildid = ?', [guild.id])
|
||||||
|
.catch((e) => utils.commonLoggers.dbError('guildDelete.ts:47', 'delete from', e));
|
||||||
dbClient
|
dbClient
|
||||||
.execute('DELETE FROM aliases WHERE guildid = ? AND userid = ?', [guild.id, 0n])
|
.execute('DELETE FROM aliases WHERE guildid = ? AND userid = ?', [guild.id, 0n])
|
||||||
.catch((e) => utils.commonLoggers.dbError('guildDelete.ts:45', 'delete from', e));
|
.catch((e) => utils.commonLoggers.dbError('guildDelete.ts:50', 'delete from', e));
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
} from '@discordeno';
|
} from '@discordeno';
|
||||||
import { log, LogTypes as LT } from '@Log4Deno';
|
import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
|
import config from '~config';
|
||||||
|
|
||||||
import { Modifiers } from 'artigen/dice/getModifiers.ts';
|
import { Modifiers } from 'artigen/dice/getModifiers.ts';
|
||||||
|
|
||||||
import { repeatRollCustomId } from 'artigen/managers/handler/workerComplete.ts';
|
import { repeatRollCustomId } from 'artigen/managers/handler/workerComplete.ts';
|
||||||
|
@ -26,6 +28,8 @@ import { commands, slashCommandDetails } from 'commands/_index.ts';
|
||||||
|
|
||||||
import { generateHelpMessage, helpCustomId } from 'commands/helpLibrary/generateHelpMessage.ts';
|
import { generateHelpMessage, helpCustomId } from 'commands/helpLibrary/generateHelpMessage.ts';
|
||||||
|
|
||||||
|
import { repeatList } from 'db/common.ts';
|
||||||
|
|
||||||
import { failColor } from 'embeds/colors.ts';
|
import { failColor } from 'embeds/colors.ts';
|
||||||
|
|
||||||
import { messageCreateHandler } from 'events/messageCreate.ts';
|
import { messageCreateHandler } from 'events/messageCreate.ts';
|
||||||
|
@ -83,9 +87,10 @@ export const interactionCreateHandler = async (interaction: Interaction) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.data.customId.startsWith(repeatRollCustomId) && interaction.message) {
|
if (interaction.data.customId.startsWith(repeatRollCustomId) && interaction.message) {
|
||||||
|
const missingUserId = 'missingUserId';
|
||||||
const ownerId = interaction.data.customId.split(InteractionValueSeparator)[1] ?? 'missingOwnerId';
|
const ownerId = interaction.data.customId.split(InteractionValueSeparator)[1] ?? 'missingOwnerId';
|
||||||
const userInteractingId = interaction.member?.user.id ?? interaction.user?.id ?? 'missingUserId';
|
const userInteractingId = interaction.member?.user.id ?? interaction.user?.id ?? missingUserId;
|
||||||
if (ownerId === userInteractingId) {
|
if (ownerId === userInteractingId || (userInteractingId !== missingUserId && repeatList.includes(BigInt(interaction.guildId ?? '0')))) {
|
||||||
const botMsg: DiscordenoMessage = await structures.createDiscordenoMessage(interaction.message);
|
const botMsg: DiscordenoMessage = await structures.createDiscordenoMessage(interaction.message);
|
||||||
if (botMsg && botMsg.messageReference) {
|
if (botMsg && botMsg.messageReference) {
|
||||||
const rollMsg = await getMessage(BigInt(botMsg.messageReference.channelId ?? '0'), BigInt(botMsg.messageReference.messageId ?? '0')).catch((e) =>
|
const rollMsg = await getMessage(BigInt(botMsg.messageReference.channelId ?? '0'), BigInt(botMsg.messageReference.messageId ?? '0')).catch((e) =>
|
||||||
|
@ -98,7 +103,11 @@ export const interactionCreateHandler = async (interaction: Interaction) => {
|
||||||
);
|
);
|
||||||
if (rollMsg && !rollMsg.isBot) {
|
if (rollMsg && !rollMsg.isBot) {
|
||||||
ackInteraction(interaction);
|
ackInteraction(interaction);
|
||||||
|
if (ownerId === userInteractingId) {
|
||||||
messageCreateHandler(rollMsg);
|
messageCreateHandler(rollMsg);
|
||||||
|
} else {
|
||||||
|
messageCreateHandler(rollMsg, BigInt(userInteractingId));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +129,28 @@ export const interactionCreateHandler = async (interaction: Interaction) => {
|
||||||
}
|
}
|
||||||
rollStr += ` ${Modifiers.YVars} ${yVarVals.join(',')}`;
|
rollStr += ` ${Modifiers.YVars} ${yVarVals.join(',')}`;
|
||||||
}
|
}
|
||||||
commands.roll(interaction, rollStr.split(argSpacesSplitRegex), '');
|
|
||||||
|
let responseInteraction: Interaction | DiscordenoMessage = interaction;
|
||||||
|
if (botMsg && botMsg.messageReference) {
|
||||||
|
const rollMsg = await getMessage(BigInt(botMsg.messageReference.channelId ?? '0'), BigInt(botMsg.messageReference.messageId ?? '0')).catch((e) =>
|
||||||
|
utils.commonLoggers.messageGetError(
|
||||||
|
'interactionCreate.ts:92',
|
||||||
|
botMsg.messageReference?.channelId ?? '0',
|
||||||
|
botMsg.messageReference?.messageId ?? '0',
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (rollMsg) {
|
||||||
|
ackInteraction(interaction);
|
||||||
|
responseInteraction = rollMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ownerId === userInteractingId) {
|
||||||
|
commands.roll(responseInteraction, rollStr.split(argSpacesSplitRegex), '');
|
||||||
|
} else {
|
||||||
|
commands.roll(responseInteraction, rollStr.split(argSpacesSplitRegex), '', BigInt(userInteractingId), BigInt(ownerId));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +163,9 @@ export const interactionCreateHandler = async (interaction: Interaction) => {
|
||||||
{
|
{
|
||||||
color: failColor,
|
color: failColor,
|
||||||
title: 'Not Allowed!',
|
title: 'Not Allowed!',
|
||||||
description: 'Only the original user that requested this roll can repeat it.',
|
description: `Only the original user that requested this roll can repeat it.
|
||||||
|
|
||||||
|
Do you want to be able to use the \`Repeat Roll\` button on any rolls from anyone? Ask your Guild Owner or Admins to run \`${config.prefix}repeat enable\`. More information can be found in the help library under \`Unrestricted Repeat\`.`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -220,6 +252,12 @@ export const interactionCreateHandler = async (interaction: Interaction) => {
|
||||||
commands.toggleInline(interaction, [subCommand]);
|
commands.toggleInline(interaction, [subCommand]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case slashCommandDetails.toggleRepeatSC.name: {
|
||||||
|
const option = (interaction.data.options as ApplicationCommandInteractionDataOptionSubCommand[])?.shift();
|
||||||
|
const subCommand = option ? option.name : '';
|
||||||
|
commands.toggleRepeat(interaction, [subCommand]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case slashCommandDetails.versionSC.name:
|
case slashCommandDetails.versionSC.name:
|
||||||
commands.version(interaction);
|
commands.version(interaction);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { commands } from 'commands/_index.ts';
|
||||||
import { ignoreList, inlineList } from 'db/common.ts';
|
import { ignoreList, inlineList } from 'db/common.ts';
|
||||||
import { argSpacesSplitRegex } from 'artigen/utils/escape.ts';
|
import { argSpacesSplitRegex } from 'artigen/utils/escape.ts';
|
||||||
|
|
||||||
export const messageCreateHandler = (message: DiscordenoMessage) => {
|
export const messageCreateHandler = (message: DiscordenoMessage, overrideAuthor?: bigint) => {
|
||||||
// Ignore all other bots
|
// Ignore all other bots
|
||||||
if (message.isBot) return;
|
if (message.isBot) return;
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ export const messageCreateHandler = (message: DiscordenoMessage) => {
|
||||||
.split(argSpacesSplitRegex)
|
.split(argSpacesSplitRegex)
|
||||||
.filter((x) => x),
|
.filter((x) => x),
|
||||||
'',
|
'',
|
||||||
|
overrideAuthor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// return as we are done handling this message
|
// return as we are done handling this message
|
||||||
|
@ -160,11 +161,16 @@ export const messageCreateHandler = (message: DiscordenoMessage) => {
|
||||||
// Manage and roll using aliases
|
// Manage and roll using aliases
|
||||||
commands.alias(message, argSpaces);
|
commands.alias(message, argSpaces);
|
||||||
break;
|
break;
|
||||||
|
case 'repeat':
|
||||||
|
// [[repeat arg
|
||||||
|
// Enable or Disable unrestricted repeat rolling
|
||||||
|
commands.toggleRepeat(message, args);
|
||||||
|
break;
|
||||||
case 'roll':
|
case 'roll':
|
||||||
case 'r':
|
case 'r':
|
||||||
// [[roll or [[r
|
// [[roll or [[r
|
||||||
// Dice rolling commence!
|
// Dice rolling commence!
|
||||||
commands.roll(message, argSpaces, '');
|
commands.roll(message, argSpaces, '', overrideAuthor);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Non-standard commands
|
// Non-standard commands
|
||||||
|
@ -175,7 +181,7 @@ export const messageCreateHandler = (message: DiscordenoMessage) => {
|
||||||
} else if (command && `${command}${args.join('')}`.includes(config.postfix)) {
|
} else if (command && `${command}${args.join('')}`.includes(config.postfix)) {
|
||||||
// [[roll]]
|
// [[roll]]
|
||||||
// Dice rolling commence!
|
// Dice rolling commence!
|
||||||
commands.roll(message, argSpaces, command);
|
commands.roll(message, argSpaces, `${config.prefix}${command}`, overrideAuthor);
|
||||||
} else if (command) {
|
} else if (command) {
|
||||||
// [[emoji or [[emoji-alias
|
// [[emoji or [[emoji-alias
|
||||||
// Check if the unhandled command is an emoji request
|
// Check if the unhandled command is an emoji request
|
||||||
|
|
|
@ -22,6 +22,7 @@ const sendOrInteract = async (
|
||||||
callLocation: string,
|
callLocation: string,
|
||||||
payload: CreateMessage,
|
payload: CreateMessage,
|
||||||
tryGetOriginal = false,
|
tryGetOriginal = false,
|
||||||
|
mentionUser = true,
|
||||||
): Promise<void | DiscordenoMessage> => {
|
): Promise<void | DiscordenoMessage> => {
|
||||||
let newMsg;
|
let newMsg;
|
||||||
if (hasOwnProperty(msgOrInt, 'token')) {
|
if (hasOwnProperty(msgOrInt, 'token')) {
|
||||||
|
@ -32,7 +33,7 @@ const sendOrInteract = async (
|
||||||
}).catch((e: Error) => messageSendError(callLocation, interaction, e));
|
}).catch((e: Error) => messageSendError(callLocation, interaction, e));
|
||||||
if (tryGetOriginal) newMsg = await getOriginalInteractionResponse(interaction.token);
|
if (tryGetOriginal) newMsg = await getOriginalInteractionResponse(interaction.token);
|
||||||
} else {
|
} else {
|
||||||
newMsg = await msgOrInt.reply(payload).catch((e: Error) => messageSendError(callLocation, msgOrInt, e));
|
newMsg = await msgOrInt.reply(payload, mentionUser).catch((e: Error) => messageSendError(callLocation, msgOrInt, e));
|
||||||
}
|
}
|
||||||
return newMsg;
|
return newMsg;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue