Implement full roll alias system with support for yVars, currently untested. Additionally: made numbers in code more readable, change indentation to spaces in db init files, fix simulatedNominal system defaults to be config driven
This commit is contained in:
parent
fedba62d52
commit
4bbdb59f3d
|
@ -36,9 +36,12 @@
|
|||
"oper",
|
||||
"ovad",
|
||||
"PEMDAS",
|
||||
"ralias",
|
||||
"Rehost",
|
||||
"Rehosts",
|
||||
"resultid",
|
||||
"rolla",
|
||||
"rollalias",
|
||||
"rolldecorators",
|
||||
"rollhelp",
|
||||
"rollstr",
|
||||
|
@ -46,11 +49,13 @@
|
|||
"sproc",
|
||||
"Tarantallegra",
|
||||
"tinyint",
|
||||
"tinytext",
|
||||
"unauthorised",
|
||||
"unban",
|
||||
"xcwody",
|
||||
"xdydz",
|
||||
"xdydzracsq",
|
||||
"xovady"
|
||||
"xovady",
|
||||
"yvar"
|
||||
]
|
||||
}
|
|
@ -8,10 +8,18 @@ export const config = {
|
|||
postfix: ']]', // Postfix for rolling command
|
||||
limits: {
|
||||
// Limits for the bot functions
|
||||
maxLoops: 1000000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk.
|
||||
alias: {
|
||||
// Roll Alias system
|
||||
free: {
|
||||
user: 100, // Allows users to have 100 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.
|
||||
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: 300000, // Maximum time before the bot kills a worker thread in ms
|
||||
simulatedNominal: 100000, // Number of loops to run for simulating a nominal
|
||||
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
|
||||
maxSimulatedNominal: 100_000, // Max number of loops a user can specify for simulating a nominal
|
||||
},
|
||||
api: {
|
||||
// Setting for the built-in API
|
||||
|
@ -19,7 +27,7 @@ export const config = {
|
|||
publicDomain: 'http://example.com/', // Public domain that the API is behind, should end with a /
|
||||
port: 8080, // Port for the API to listen on
|
||||
supportURL: 'your_support_url_for_api_abuse', // Fill this in with the way you wish to be contacted when somebody needs to report API key abuse
|
||||
rateLimitTime: 10000, // Time range for how often the API rate limits will be lifted (time in ms)
|
||||
rateLimitTime: 10_000, // Time range for how often the API rate limits will be lifted (time in ms)
|
||||
rateLimitCnt: 10, // Amount of requests that can be made (successful or not) during above time range before getting rate limited
|
||||
admin: 0n, // Discord user ID of the bot admin, this user will be the user that can ban/unban user/channel combos and API keys
|
||||
adminKey: 'your_25char_api_token', // API Key generated by nanoid that is 25 char long, this gets pre-populated into all_keys
|
||||
|
|
224
db/initialize.ts
224
db/initialize.ts
|
@ -6,7 +6,6 @@ import dbClient from 'db/client.ts';
|
|||
|
||||
console.log('Attempting to create DB');
|
||||
await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`);
|
||||
console.log('test');
|
||||
await dbClient.execute(`USE ${config.db.name}`);
|
||||
console.log('DB created');
|
||||
|
||||
|
@ -22,173 +21,190 @@ await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`);
|
|||
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 allow_inline;`);
|
||||
await dbClient.execute(`DROP TABLE IF EXISTS aliases;`);
|
||||
console.log('Tables dropped');
|
||||
|
||||
// Holds all aliases that have been created
|
||||
console.log('Attempting to create table aliases');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE aliases (
|
||||
guildid bigint unsigned NOT NULL,
|
||||
userid bigint unsigned NOT NULL,
|
||||
aliasName varchar(100) NOT NULL,
|
||||
rollStr varchar(4000) NOT NULL,
|
||||
yVarCnt tinyint unsigned NOT NULL,
|
||||
premium tinyint(1) NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (guildid, userid, aliasName)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Holds guilds that have explicitly allowed inline rolls
|
||||
console.log('Attempting to create table allow_inline');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE allow_inline (
|
||||
guildid bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (guildid),
|
||||
UNIQUE KEY allow_inline_guildid_UNIQUE (guildid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE allow_inline (
|
||||
guildid bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (guildid),
|
||||
UNIQUE KEY allow_inline_guildid_UNIQUE (guildid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Table to hold list of users who want to be ignored by the bot
|
||||
console.log('Attempting to create table ignore_list');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE ignore_list (
|
||||
userid bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (userid),
|
||||
UNIQUE KEY ignore_list_userid_UNIQUE (userid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE ignore_list (
|
||||
userid bigint unsigned NOT NULL,
|
||||
PRIMARY KEY (userid),
|
||||
UNIQUE KEY ignore_list_userid_UNIQUE (userid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Light telemetry on how many commands have been run
|
||||
console.log('Attempting to create table command_cnt');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE command_cnt (
|
||||
command char(20) NOT NULL,
|
||||
count bigint unsigned NOT NULL DEFAULT 0,
|
||||
hourlyRate float unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (command),
|
||||
UNIQUE KEY command_cnt_command_UNIQUE (command)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE command_cnt (
|
||||
command char(20) NOT NULL,
|
||||
count bigint unsigned NOT NULL DEFAULT 0,
|
||||
hourlyRate float unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (command),
|
||||
UNIQUE KEY command_cnt_command_UNIQUE (command)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
console.log('Attempt creating increment count Stored Procedure');
|
||||
await dbClient.execute(`
|
||||
CREATE PROCEDURE INC_CNT(
|
||||
IN cmd CHAR(20)
|
||||
)
|
||||
BEGIN
|
||||
declare oldcnt bigint unsigned;
|
||||
set oldcnt = (SELECT count FROM command_cnt WHERE command = cmd);
|
||||
UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd;
|
||||
END
|
||||
CREATE PROCEDURE INC_CNT(
|
||||
IN cmd CHAR(20)
|
||||
)
|
||||
BEGIN
|
||||
declare oldcnt bigint unsigned;
|
||||
set oldcnt = (SELECT count FROM command_cnt WHERE command = cmd);
|
||||
UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd;
|
||||
END
|
||||
`);
|
||||
console.log('Stored Procedure created');
|
||||
|
||||
// Holds daily average of commands
|
||||
console.log('Attempting to create table roll_time_heatmap');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE roll_time_heatmap (
|
||||
hour tinyint(1) unsigned NOT NULL,
|
||||
sunday bigint unsigned NOT NULL DEFAULT 0,
|
||||
monday bigint unsigned NOT NULL DEFAULT 0,
|
||||
tuesday bigint unsigned NOT NULL DEFAULT 0,
|
||||
wednesday bigint unsigned NOT NULL DEFAULT 0,
|
||||
thursday bigint unsigned NOT NULL DEFAULT 0,
|
||||
friday bigint unsigned NOT NULL DEFAULT 0,
|
||||
saturday bigint unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (hour),
|
||||
UNIQUE KEY roll_time_heatmap_hour_UNIQUE (hour)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE roll_time_heatmap (
|
||||
hour tinyint(1) unsigned NOT NULL,
|
||||
sunday bigint unsigned NOT NULL DEFAULT 0,
|
||||
monday bigint unsigned NOT NULL DEFAULT 0,
|
||||
tuesday bigint unsigned NOT NULL DEFAULT 0,
|
||||
wednesday bigint unsigned NOT NULL DEFAULT 0,
|
||||
thursday bigint unsigned NOT NULL DEFAULT 0,
|
||||
friday bigint unsigned NOT NULL DEFAULT 0,
|
||||
saturday bigint unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (hour),
|
||||
UNIQUE KEY roll_time_heatmap_hour_UNIQUE (hour)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
console.log('Attempt creating increment heatmap Stored Procedure');
|
||||
await dbClient.execute(`
|
||||
CREATE PROCEDURE INC_HEATMAP(
|
||||
IN dy varchar(10),
|
||||
IN hr tinyint(1)
|
||||
)
|
||||
BEGIN
|
||||
SET @s1=CONCAT('SELECT ',dy,' FROM roll_time_heatmap WHERE hour = ',hr,' INTO @oldcnt');
|
||||
PREPARE stmt1 FROM @s1;
|
||||
EXECUTE stmt1;
|
||||
DEALLOCATE PREPARE stmt1;
|
||||
CREATE PROCEDURE INC_HEATMAP(
|
||||
IN dy varchar(10),
|
||||
IN hr tinyint(1)
|
||||
)
|
||||
BEGIN
|
||||
SET @s1=CONCAT('SELECT ',dy,' FROM roll_time_heatmap WHERE hour = ',hr,' INTO @oldcnt');
|
||||
PREPARE stmt1 FROM @s1;
|
||||
EXECUTE stmt1;
|
||||
DEALLOCATE PREPARE stmt1;
|
||||
|
||||
SET @s2=CONCAT('UPDATE roll_time_heatmap SET ',dy,' = @oldcnt + 1 WHERE hour = ',hr);
|
||||
PREPARE stmt2 FROM @s2;
|
||||
EXECUTE stmt2;
|
||||
DEALLOCATE PREPARE stmt2;
|
||||
END
|
||||
SET @s2=CONCAT('UPDATE roll_time_heatmap SET ',dy,' = @oldcnt + 1 WHERE hour = ',hr);
|
||||
PREPARE stmt2 FROM @s2;
|
||||
EXECUTE stmt2;
|
||||
DEALLOCATE PREPARE stmt2;
|
||||
END
|
||||
`);
|
||||
console.log('Stored Procedure created');
|
||||
|
||||
// Roll log, holds rolls when requests
|
||||
console.log('Attempting to create table roll_log');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE roll_log (
|
||||
id int unsigned NOT NULL AUTO_INCREMENT,
|
||||
input text NOT NULL,
|
||||
resultid bigint NULL,
|
||||
result longtext NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
api tinyint(1) NOT NULL,
|
||||
error tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY roll_log_id_UNIQUE (id),
|
||||
UNIQUE KEY roll_log_resultid_UNIQUE (resultid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE roll_log (
|
||||
id int unsigned NOT NULL AUTO_INCREMENT,
|
||||
input text NOT NULL,
|
||||
resultid bigint NULL,
|
||||
result longtext NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
api tinyint(1) NOT NULL,
|
||||
error tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY roll_log_id_UNIQUE (id),
|
||||
UNIQUE KEY roll_log_resultid_UNIQUE (resultid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Api guild settings
|
||||
console.log('Attempting to create table allowed_guilds');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE allowed_guilds (
|
||||
guildid bigint unsigned NOT NULL,
|
||||
channelid bigint unsigned NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 0,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
hidewarn tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (guildid, channelid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE allowed_guilds (
|
||||
guildid bigint unsigned NOT NULL,
|
||||
channelid bigint unsigned NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 0,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
hidewarn tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (guildid, channelid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Api keys
|
||||
console.log('Attempting to create table all_keys');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE all_keys (
|
||||
userid bigint unsigned NOT NULL,
|
||||
apiKey char(25) NOT NULL,
|
||||
deleteCode char(10) NULL,
|
||||
email char(255) NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 1,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (userid),
|
||||
UNIQUE KEY all_keys_userid_UNIQUE (userid),
|
||||
UNIQUE KEY all_keys_apiKey_UNIQUE (apiKey),
|
||||
UNIQUE KEY all_keys_email_UNIQUE (email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE all_keys (
|
||||
userid bigint unsigned NOT NULL,
|
||||
apiKey char(25) NOT NULL,
|
||||
deleteCode char(10) NULL,
|
||||
email char(255) NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 1,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (userid),
|
||||
UNIQUE KEY all_keys_userid_UNIQUE (userid),
|
||||
UNIQUE KEY all_keys_apiKey_UNIQUE (apiKey),
|
||||
UNIQUE KEY all_keys_email_UNIQUE (email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Api user settings
|
||||
console.log('Attempting to create table allowed_channels');
|
||||
await dbClient.execute(`
|
||||
CREATE TABLE allowed_channels (
|
||||
userid bigint unsigned NOT NULL,
|
||||
channelid bigint unsigned NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 1,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (userid, channelid),
|
||||
CONSTRAINT allowed_channels_userid_FK FOREIGN KEY (userid) REFERENCES all_keys (userid) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE TABLE allowed_channels (
|
||||
userid bigint unsigned NOT NULL,
|
||||
channelid bigint unsigned NOT NULL,
|
||||
createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
active tinyint(1) NOT NULL DEFAULT 1,
|
||||
banned tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (userid, channelid),
|
||||
CONSTRAINT allowed_channels_userid_FK FOREIGN KEY (userid) REFERENCES all_keys (userid) ON DELETE RESTRICT ON UPDATE RESTRICT
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`);
|
||||
console.log('Table created');
|
||||
|
||||
// Database sizes view
|
||||
console.log('Attempting to create view db_size');
|
||||
await dbClient.execute(`
|
||||
CREATE VIEW db_size AS
|
||||
SELECT
|
||||
table_name AS "table",
|
||||
ROUND(((data_length + index_length) / 1024 / 1024), 3) AS "size",
|
||||
table_rows AS "rows"
|
||||
FROM information_schema.TABLES
|
||||
WHERE
|
||||
table_schema = "${config.db.name}"
|
||||
AND table_name <> "db_size";
|
||||
CREATE VIEW db_size AS
|
||||
SELECT
|
||||
table_name AS "table",
|
||||
ROUND(((data_length + index_length) / 1024 / 1024), 3) AS "size",
|
||||
table_rows AS "rows"
|
||||
FROM information_schema.TABLES
|
||||
WHERE
|
||||
table_schema = "${config.db.name}"
|
||||
AND table_name <> "db_size";
|
||||
`);
|
||||
console.log('View Created');
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ console.log('Insertion done');
|
|||
|
||||
console.log('Attempting to insert default commands into command_cnt');
|
||||
const commands = [
|
||||
'alias',
|
||||
'api',
|
||||
'audit',
|
||||
'emojis',
|
||||
|
|
|
@ -63,6 +63,7 @@ export interface RollModifiers {
|
|||
rollDist: boolean;
|
||||
numberVariables: boolean;
|
||||
customDiceShapes: CustomDiceShapes;
|
||||
yVars: Map<string, number>;
|
||||
apiWarn: string;
|
||||
valid: boolean;
|
||||
error: Error;
|
||||
|
|
|
@ -46,6 +46,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
|||
rollDist: false,
|
||||
numberVariables: false,
|
||||
customDiceShapes: new Map<string, number[]>(),
|
||||
yVars: new Map<string, number>(),
|
||||
apiWarn: '',
|
||||
valid: true,
|
||||
error: new Error(),
|
||||
|
@ -88,7 +89,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
|||
|
||||
modifiers.simulatedNominal = parseInt(args[i]);
|
||||
} else {
|
||||
modifiers.simulatedNominal = 10000;
|
||||
modifiers.simulatedNominal = config.limits.defaultSimulatedNominal;
|
||||
}
|
||||
break;
|
||||
case Modifiers.ConfirmCrit:
|
||||
|
@ -225,9 +226,9 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
|
|||
}
|
||||
|
||||
// simulatedNominal cannot be greater than config.limits.simulatedNominal
|
||||
if (modifiers.simulatedNominal > config.limits.simulatedNominal) {
|
||||
if (modifiers.simulatedNominal > config.limits.maxSimulatedNominal) {
|
||||
modifiers.error.name = 'SimNominalTooBig';
|
||||
modifiers.error.message = `Number of iterations for \`simulatedNominal\` cannot be greater than \`${config.limits.simulatedNominal}\``;
|
||||
modifiers.error.message = `Number of iterations for \`simulatedNominal\` cannot be greater than \`${config.limits.maxSimulatedNominal}\``;
|
||||
modifiers.valid = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import { botId, DiscordenoMessage, Embed, FileContent, sendDirectMessage, sendMe
|
|||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import config from '~config';
|
||||
import { DEVMODE } from '~flags';
|
||||
|
||||
import { SolvedRoll } from 'artigen/artigen.d.ts';
|
||||
|
||||
|
@ -23,6 +22,12 @@ import utils from 'utils/utils.ts';
|
|||
import { infoColor1 } from 'embeds/colors.ts';
|
||||
import { basicReducer } from 'artigen/utils/reducers.ts';
|
||||
|
||||
const getUserIdForEmbed = (rollRequest: QueuedRoll): bigint => {
|
||||
if (rollRequest.apiRoll) return rollRequest.api.userId;
|
||||
if (rollRequest.ddRoll) return rollRequest.dd.originalMessage.authorId;
|
||||
return 0n;
|
||||
};
|
||||
|
||||
export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>, workerTimeout: number, rollRequest: QueuedRoll) => {
|
||||
let apiErroredOut = false;
|
||||
try {
|
||||
|
@ -32,12 +37,8 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
const returnMsg = workerMessage.data;
|
||||
loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnMsg.line1.length} |&| ${returnMsg.line2.length} |&| ${returnMsg.line3.length} `);
|
||||
loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnMsg.line1} |&| ${returnMsg.line2} |&| ${returnMsg.line3} `);
|
||||
const pubEmbedDetails = generateRollEmbed(
|
||||
rollRequest.apiRoll ? rollRequest.api.userId : rollRequest.dd.originalMessage.authorId,
|
||||
returnMsg,
|
||||
rollRequest.modifiers,
|
||||
);
|
||||
const gmEmbedDetails = generateRollEmbed(rollRequest.apiRoll ? rollRequest.api.userId : rollRequest.dd.originalMessage.authorId, returnMsg, {
|
||||
const pubEmbedDetails = generateRollEmbed(getUserIdForEmbed(rollRequest), returnMsg, rollRequest.modifiers);
|
||||
const gmEmbedDetails = generateRollEmbed(getUserIdForEmbed(rollRequest), returnMsg, {
|
||||
...rollRequest.modifiers,
|
||||
gmRoll: false,
|
||||
});
|
||||
|
@ -81,24 +82,34 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
if (returnMsg.error) {
|
||||
if (rollRequest.apiRoll) {
|
||||
rollRequest.api.resolve(stdResp.InternalServerError(returnMsg.errorMsg));
|
||||
} else {
|
||||
} else if (rollRequest.ddRoll) {
|
||||
rollRequest.dd.myResponse.edit({ embeds: pubEmbeds });
|
||||
} else if (rollRequest.testRoll) {
|
||||
rollRequest.test.resolve({
|
||||
error: true,
|
||||
errorMsg: returnMsg.errorMsg,
|
||||
errorCode: returnMsg.errorCode,
|
||||
});
|
||||
}
|
||||
|
||||
if (rollRequest.apiRoll || (DEVMODE && config.logRolls)) {
|
||||
if (rollRequest.apiRoll) {
|
||||
// If enabled, log rolls so we can see what went wrong
|
||||
dbClient
|
||||
.execute(queries.insertRollLogCmd(rollRequest.apiRoll ? 1 : 0, 1), [
|
||||
rollRequest.originalCommand,
|
||||
returnMsg.errorCode,
|
||||
rollRequest.apiRoll ? null : rollRequest.dd.myResponse.id,
|
||||
])
|
||||
.execute(queries.insertRollLogCmd(rollRequest.apiRoll ? 1 : 0, 1), [rollRequest.originalCommand, returnMsg.errorCode, null])
|
||||
.catch((e) => utils.commonLoggers.dbError('rollQueue.ts:82', 'insert into', e));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Test roll will assume that messages send successfully
|
||||
if (rollRequest.testRoll) {
|
||||
rollRequest.test.resolve({
|
||||
error: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let newMsg: DiscordenoMessage | void = undefined;
|
||||
// Determine if we are to send a GM roll or a normal roll
|
||||
if (rollRequest.modifiers.gmRoll) {
|
||||
|
@ -213,7 +224,7 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
}
|
||||
} catch (e) {
|
||||
log(LT.ERROR, `Unhandled rollRequest Error: ${JSON.stringify(e)}`);
|
||||
if (!rollRequest.apiRoll) {
|
||||
if (rollRequest.ddRoll) {
|
||||
rollRequest.dd.myResponse.edit({
|
||||
embeds: [
|
||||
(
|
||||
|
@ -230,9 +241,14 @@ export const onWorkerComplete = async (workerMessage: MessageEvent<SolvedRoll>,
|
|||
).embed,
|
||||
],
|
||||
});
|
||||
}
|
||||
if (rollRequest.apiRoll && !apiErroredOut) {
|
||||
} else if (rollRequest.apiRoll && !apiErroredOut) {
|
||||
rollRequest.api.resolve(stdResp.InternalServerError(JSON.stringify(e)));
|
||||
} else if (rollRequest.testRoll) {
|
||||
rollRequest.test.resolve({
|
||||
error: true,
|
||||
errorMsg: 'Something weird went wrong.',
|
||||
errorCode: 'UnhandledWorkerComplete',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ export const terminateWorker = async (rollWorker: Worker, rollRequest: QueuedRol
|
|||
|
||||
if (rollRequest.apiRoll) {
|
||||
rollRequest.api.resolve(stdResp.RequestTimeout('Roll took too long to process, try breaking roll down into simpler parts'));
|
||||
} else {
|
||||
} else if (rollRequest.ddRoll) {
|
||||
rollRequest.dd.myResponse
|
||||
.edit({
|
||||
embeds: [
|
||||
|
@ -35,5 +35,11 @@ export const terminateWorker = async (rollWorker: Worker, rollRequest: QueuedRol
|
|||
],
|
||||
})
|
||||
.catch((e) => utils.commonLoggers.messageEditError('rollQueue.ts:51', rollRequest.dd.myResponse, e));
|
||||
} else if (rollRequest.testRoll) {
|
||||
rollRequest.test.resolve({
|
||||
error: true,
|
||||
errorCode: 'TooComplex',
|
||||
errorMsg: 'Error: Roll took too long to process, try breaking roll down into simpler parts',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,19 +8,40 @@ interface BaseQueuedRoll {
|
|||
modifiers: RollModifiers;
|
||||
originalCommand: string;
|
||||
}
|
||||
export interface ApiQueuedRoll extends BaseQueuedRoll {
|
||||
interface ApiQueuedRoll extends BaseQueuedRoll {
|
||||
apiRoll: true;
|
||||
ddRoll: false;
|
||||
testRoll: false;
|
||||
api: {
|
||||
resolve: (value: Response | PromiseLike<Response>) => void;
|
||||
channelId: bigint;
|
||||
userId: bigint;
|
||||
};
|
||||
}
|
||||
export interface DDQueuedRoll extends BaseQueuedRoll {
|
||||
interface DDQueuedRoll extends BaseQueuedRoll {
|
||||
apiRoll: false;
|
||||
ddRoll: true;
|
||||
testRoll: false;
|
||||
dd: {
|
||||
myResponse: DiscordenoMessage;
|
||||
originalMessage: DiscordenoMessage;
|
||||
};
|
||||
}
|
||||
export type QueuedRoll = ApiQueuedRoll | DDQueuedRoll;
|
||||
interface TestResultFail {
|
||||
error: true;
|
||||
errorMsg: string;
|
||||
errorCode: string;
|
||||
}
|
||||
interface TestResultSuccess {
|
||||
error: false;
|
||||
}
|
||||
export type TestResults = TestResultFail | TestResultSuccess;
|
||||
interface TestQueuedRoll extends BaseQueuedRoll {
|
||||
apiRoll: false;
|
||||
ddRoll: false;
|
||||
testRoll: true;
|
||||
test: {
|
||||
resolve: (value: TestResults) => void;
|
||||
};
|
||||
}
|
||||
export type QueuedRoll = ApiQueuedRoll | DDQueuedRoll | TestQueuedRoll;
|
||||
|
|
|
@ -16,25 +16,24 @@ const rollQueue: Array<QueuedRoll> = [];
|
|||
|
||||
// Runs the roll or queues it depending on how many workers are currently running
|
||||
export const sendRollRequest = (rollRequest: QueuedRoll) => {
|
||||
if (rollRequest.apiRoll) {
|
||||
handleRollRequest(rollRequest);
|
||||
} else if (!rollQueue.length && getWorkerCnt() < config.limits.maxWorkers) {
|
||||
if (!rollQueue.length && getWorkerCnt() < config.limits.maxWorkers) {
|
||||
handleRollRequest(rollRequest);
|
||||
} else {
|
||||
rollQueue.push(rollRequest);
|
||||
rollRequest.dd.myResponse
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: infoColor2,
|
||||
title: `${config.name} currently has its hands full and has queued your roll.`,
|
||||
description: `There are currently ${getWorkerCnt() + rollQueue.length} rolls ahead of this roll.
|
||||
rollRequest.ddRoll &&
|
||||
rollRequest.dd.myResponse
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: infoColor2,
|
||||
title: `${config.name} currently has its hands full and has queued your roll.`,
|
||||
description: `There are currently ${getWorkerCnt() + rollQueue.length} rolls ahead of this roll.
|
||||
|
||||
The results for this roll will replace this message when it is done.`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:197', rollRequest.dd.myResponse, e));
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:197', rollRequest.dd.myResponse, e));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -46,11 +45,12 @@ setInterval(() => {
|
|||
);
|
||||
if (rollQueue.length && getWorkerCnt() < config.limits.maxWorkers) {
|
||||
const rollRequest = rollQueue.shift();
|
||||
if (rollRequest && !rollRequest.apiRoll) {
|
||||
rollRequest.dd.myResponse.edit(rollingEmbed).catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:208', rollRequest.dd.myResponse, e));
|
||||
handleRollRequest(rollRequest);
|
||||
} else if (rollRequest && rollRequest.apiRoll) {
|
||||
if (rollRequest) {
|
||||
rollRequest.ddRoll &&
|
||||
rollRequest.dd.myResponse
|
||||
.edit(rollingEmbed)
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:208', rollRequest.dd.myResponse, e));
|
||||
handleRollRequest(rollRequest);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}, 1_000);
|
||||
|
|
|
@ -179,6 +179,19 @@ export const tokenizeMath = (
|
|||
} else {
|
||||
throw new Error(`IllegalVariable_${curMathConfStr}`);
|
||||
}
|
||||
} else if (/(y\d+(\.\d*)?)/.test(curMathConfStr)) {
|
||||
// Identify when someone is using a variable from alias input
|
||||
if (curMathConfStr.includes('.')) {
|
||||
// Verify someone did not enter y1.1 as a variable
|
||||
throw new Error(`IllegalVariable_${curMathConfStr}`);
|
||||
}
|
||||
|
||||
const yValue = modifiers.yVars.get(curMathConfStr);
|
||||
if (typeof yValue === 'number') {
|
||||
mathConf[i] = yValue;
|
||||
} else {
|
||||
throw new Error(`VariableMissingValue_${curMathConfStr}`);
|
||||
}
|
||||
} else if (![...allOps, ...legalMathOperators].includes(curMathConfStr)) {
|
||||
// If nothing else has handled it by now, try it as a roll
|
||||
const executedRoll = executeRoll(curMathConfStr, modifiers);
|
||||
|
|
|
@ -128,7 +128,7 @@ export const generateRollDistsEmbed = (rollDists: RollDistributionMap): ArtigenE
|
|||
const rollDistTitle = 'Roll Distributions:';
|
||||
|
||||
const totalSize = fields.map((field) => field.name.length + field.value.length).reduce(basicReducer, 0);
|
||||
if (totalSize > 4000 || fields.length > 25 || fields.some((field) => field.name.length > 256 || field.value.length > 1024)) {
|
||||
if (totalSize > 4_000 || fields.length > 25 || fields.some((field) => field.name.length > 256 || field.value.length > 1024)) {
|
||||
const rollDistBlob = new Blob([fields.map((field) => `# ${field.name}\n${field.value}`).join('\n\n') as BlobPart], { type: 'text' });
|
||||
if (rollDistBlob.size > config.maxFileSize) {
|
||||
const rollDistErrDesc =
|
||||
|
@ -227,7 +227,7 @@ export const generateRollEmbed = (
|
|||
const baseDesc = `${line1Details}**${line2Details.shift()}:**\n${line2Details.join(': ')}`;
|
||||
|
||||
// Embed desc limit is 4096
|
||||
if (baseDesc.length + details.length < 4000) {
|
||||
if (baseDesc.length + details.length < 4_000) {
|
||||
// Response is valid size
|
||||
const desc = `${baseDesc}\n\n${details}`;
|
||||
return {
|
||||
|
|
|
@ -27,7 +27,7 @@ const escapePrefixPostfix = (str: string): string => str.replace(/[.*+?^${}()|[\
|
|||
export const cmdSplitRegex = new RegExp(`(${escapePrefixPostfix(config.prefix)})|(${escapePrefixPostfix(config.postfix)})`, 'g');
|
||||
|
||||
// breaks the string on the following: (\*\*) ** for exponents ([+()*/^] for basic algebra (?<![d%])% for breaking on d%%%% dice correctly (?<![rsfop!=<>])- for breaking on - correctly with fate dice) (x\d+(\.\d*)?) x# for variables
|
||||
export const mathSplitRegex = /(\*\*)|([+()*/^]|(?<![d%])%|(?<![rsfop!=<>])-)|(x\d+(\.\d*)?)/g;
|
||||
export const mathSplitRegex = /(\*\*)|([+()*/^]|(?<![d%])%|(?<![rsfop!=<>])-)|([xy]\d+(\.\d*)?)/g;
|
||||
|
||||
// Internal is used for recursive text replacement, these will always be the top level as they get replaced with config.prefix/postfix when exiting each level
|
||||
export const openInternal = '\u2045';
|
||||
|
|
|
@ -9,12 +9,20 @@ import { MathConf } from 'artigen/math/math.d.ts';
|
|||
import { closeInternal, closeInternalGrp, openInternal, openInternalGrp } from 'artigen/utils/escape.ts';
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
|
||||
const checkBalance = (conf: MathConf[], openStr: string, closeStr: string, errorType: string, getMatching: boolean, openIdx: number): number => {
|
||||
const checkBalance = (
|
||||
conf: MathConf[],
|
||||
openStr: string,
|
||||
closeStr: string,
|
||||
errorType: string,
|
||||
getMatching: boolean,
|
||||
openIdx: number,
|
||||
countLoops = true,
|
||||
): number => {
|
||||
let parenCnt = 0;
|
||||
|
||||
// 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++) {
|
||||
loopCountCheck();
|
||||
countLoops && loopCountCheck();
|
||||
loggingEnabled &&
|
||||
log(
|
||||
LT.LOG,
|
||||
|
@ -55,11 +63,11 @@ const checkBalance = (conf: MathConf[], openStr: string, closeStr: string, error
|
|||
// assertXBalance verifies the entire conf has balanced X
|
||||
export const assertGroupBalance = (conf: MathConf[]) => checkBalance(conf, '{', '}', 'Group', false, 0);
|
||||
export const assertParenBalance = (conf: MathConf[]) => checkBalance(conf, '(', ')', 'Paren', false, 0);
|
||||
export const assertPrePostBalance = (conf: MathConf[]) => checkBalance(conf, config.prefix, config.postfix, 'PrefixPostfix', false, 0);
|
||||
export const assertPrePostBalance = (conf: MathConf[], countLoops = true) => checkBalance(conf, config.prefix, config.postfix, 'PrefixPostfix', false, 0, countLoops);
|
||||
|
||||
// getMatchingXIdx gets the matching X, also partially verifies the conf has balanced X
|
||||
export const getMatchingGroupIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, '{', '}', 'Group', true, openIdx);
|
||||
export const getMatchingInternalIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, openInternal, closeInternal, 'Internal', true, openIdx);
|
||||
export const getMatchingInternalGrpIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, openInternalGrp, closeInternalGrp, 'InternalGrp', true, openIdx);
|
||||
export const getMatchingParenIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, '(', ')', 'Paren', true, openIdx);
|
||||
export const getMatchingPostfixIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, config.prefix, config.postfix, 'PrefixPostfix', true, openIdx);
|
||||
export const getMatchingPostfixIdx = (conf: MathConf[], openIdx: number, countLoops = true): number => checkBalance(conf, config.prefix, config.postfix, 'PrefixPostfix', true, openIdx, countLoops);
|
||||
|
|
|
@ -123,6 +123,9 @@ export const translateError = (solverError: Error): [string, string] => {
|
|||
case 'IllegalVariable':
|
||||
errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
|
||||
break;
|
||||
case 'VariableMissingValue':
|
||||
errorMsg = `Error: \`${errorDetails}\` is missing a valid value`;
|
||||
break;
|
||||
case 'TooManyLabels':
|
||||
errorMsg = `Error: ${config.name} can only support a maximum of \`${errorDetails}\` labels when using the dice matching options (\`m\` or \`mt\`)`;
|
||||
break;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { alias } from 'commands/aliasCmd.ts';
|
||||
import { api } from 'commands/apiCmd.ts';
|
||||
import { audit } from 'commands/audit.ts';
|
||||
import { emoji } from 'commands/emoji.ts';
|
||||
|
@ -18,6 +19,7 @@ import { toggleInline } from 'commands/toggleInline.ts';
|
|||
import { version } from 'commands/version.ts';
|
||||
|
||||
export default {
|
||||
alias,
|
||||
api,
|
||||
audit,
|
||||
emoji,
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { DiscordenoMessage } from '@discordeno';
|
||||
|
||||
import aliasCommands from 'commands/aliasCmd/_index.ts';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
import { queries } from 'db/common.ts';
|
||||
|
||||
import { failColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
export const alias = (message: DiscordenoMessage, argSpaces: string[]) => {
|
||||
// Light telemetry to see how many times a command is being run
|
||||
dbClient.execute(queries.callIncCnt('alias')).catch((e) => utils.commonLoggers.dbError('aliasCmd.ts:16', 'call sproc INC_CNT on', e));
|
||||
|
||||
// argSpaces will come in with a space or \n before every real arg, so extra shifts exist to remove them
|
||||
argSpaces.shift();
|
||||
let aliasArg = (argSpaces.shift() || '').toLowerCase().trim();
|
||||
argSpaces.shift();
|
||||
|
||||
let guildMode = false;
|
||||
if (aliasArg === 'guild') {
|
||||
guildMode = true;
|
||||
aliasArg = (argSpaces.shift() || '').toLowerCase().trim();
|
||||
argSpaces.shift();
|
||||
}
|
||||
|
||||
if (guildMode && message.guildId === 0n) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Guild Aliases can only be modified from within the desired guild.',
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasCmd.ts:38', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
// Makes sure the user is authenticated to run the API command
|
||||
switch (aliasArg) {
|
||||
case 'help':
|
||||
case 'h':
|
||||
case '?':
|
||||
case '':
|
||||
aliasCommands.help(message, guildMode);
|
||||
break;
|
||||
case 'list':
|
||||
case 'list-all':
|
||||
aliasCommands.list(message, guildMode);
|
||||
break;
|
||||
case 'add':
|
||||
case 'create':
|
||||
case 'set':
|
||||
aliasCommands.add(message, guildMode, argSpaces);
|
||||
break;
|
||||
case 'update':
|
||||
case 'replace':
|
||||
aliasCommands.update(message, guildMode, argSpaces);
|
||||
break;
|
||||
case 'preview':
|
||||
case 'view':
|
||||
aliasCommands.view(message, guildMode, argSpaces);
|
||||
break;
|
||||
case 'delete':
|
||||
case 'remove':
|
||||
aliasCommands.deleteOne(message, guildMode, argSpaces);
|
||||
break;
|
||||
case 'delete-all':
|
||||
case 'remove-all':
|
||||
aliasCommands.deleteAll(message, guildMode, argSpaces);
|
||||
break;
|
||||
case 'run':
|
||||
case 'execute':
|
||||
default:
|
||||
aliasCommands.run(message, guildMode, aliasArg, argSpaces);
|
||||
break;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import { add, update } from './aliasAddUpdate.ts';
|
||||
import { deleteAll, deleteOne } from './aliasDelete.ts';
|
||||
import { help } from 'commands/aliasCmd/aliasHelp.ts';
|
||||
import { list } from 'commands/aliasCmd/list.ts';
|
||||
import { run } from 'commands/aliasCmd/run.ts';
|
||||
import { view } from 'commands/aliasCmd/view.ts';
|
||||
|
||||
export default {
|
||||
add,
|
||||
deleteAll,
|
||||
deleteOne,
|
||||
help,
|
||||
list,
|
||||
run,
|
||||
update,
|
||||
view,
|
||||
};
|
|
@ -0,0 +1,392 @@
|
|||
import { DiscordenoMessage, EmbedField, hasGuildPermissions } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import { getModifiers } from 'artigen/dice/getModifiers.ts';
|
||||
|
||||
import { TestResults } from 'artigen/managers/manager.d.ts';
|
||||
import { sendRollRequest } from 'artigen/managers/queueManager.ts';
|
||||
|
||||
import { cmdSplitRegex } from 'artigen/utils/escape.ts';
|
||||
import { assertPrePostBalance, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
|
||||
|
||||
import { ReservedWords } from 'commands/aliasCmd/reservedWords.ts';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
|
||||
import { generateAliasError } from 'embeds/alias.ts';
|
||||
import { failColor, infoColor1, successColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
interface QueryShape {
|
||||
aliasName: string;
|
||||
}
|
||||
|
||||
const sortYVars = (a: string, b: string) => {
|
||||
if (a.length < b.length) return -1;
|
||||
if (a.length > b.length) return 1;
|
||||
if (a < b) return -1;
|
||||
if (a > b) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
const handleAddUpdate = async (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[], replaceAlias: boolean) => {
|
||||
if (guildMode && !(await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR']))) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `Error: Only Guild Owners and Admins can add/update guild aliases`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:45', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const aliasName = (argSpaces.shift() || '').trim();
|
||||
argSpaces.shift();
|
||||
|
||||
if (aliasName.length > 100) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Error: Alias Name is too long',
|
||||
description: `\`${aliasName}\` (\`${aliasName.length}\` characters) is longer than the allowed max length of \`100\` characters. Please choose a shorter alias name.`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:64', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReservedWords.includes(aliasName?.toLowerCase())) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `Error: \`${aliasName}\` is a reserved word`,
|
||||
description: `Please choose a different name for this alias.
|
||||
|
||||
You cannot use any of the following reserved words: \`${ReservedWords.join('`, `')}\`.`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:33', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
let errorOut = false;
|
||||
const query: QueryShape[] = await dbClient
|
||||
.query(
|
||||
`SELECT aliasName FROM aliases WHERE guildid = ? AND userid = ? AND aliasName = ?`,
|
||||
guildMode ? [message.guildId, 0n, aliasName.toLowerCase()] : [0n, message.authorId, aliasName.toLowerCase()],
|
||||
)
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('add.ts:44', 'query', e0);
|
||||
message
|
||||
.send(generateAliasError('DB Query Failed.', `add-q0-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:47', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
|
||||
if (!replaceAlias && query.length) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `Error: \`${aliasName}\` already exists as a ${guildMode ? 'guild' : 'personal'} alias`,
|
||||
description: 'Please choose a different name for this alias.',
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:63', message, e));
|
||||
return;
|
||||
} else if (replaceAlias && !query.length) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `Error: \`${aliasName}\` does not exist as a ${guildMode ? 'guild' : 'personal'} alias`,
|
||||
description: `If you are trying to create a new ${guildMode ? 'guild' : 'personal'} alias, please run the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}add\` followed by the desired alias name and roll string.
|
||||
|
||||
If you are trying to update an existing alias, but forgot the name, please run the following command to view all your ${guildMode ? 'guild ' : ''}aliases:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}list\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:63', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const rawRollStr = argSpaces.join('').trim();
|
||||
const newMsg: DiscordenoMessage | void = await message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: infoColor1,
|
||||
title: 'Please wait, testing your roll string . . .',
|
||||
description: `The following roll string is being tested. Once the verdict of your roll has been determined, this message will be updated.
|
||||
|
||||
\`${rawRollStr}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e) => {
|
||||
utils.commonLoggers.dbError('add.ts:78', 'query', e);
|
||||
errorOut = true;
|
||||
});
|
||||
|
||||
if (errorOut || !newMsg) return;
|
||||
|
||||
const [modifiers, remainingArgs] = getModifiers(argSpaces);
|
||||
const failedRollMsg = `The provided roll string (listed below) encountered an error. Please try this roll outside the roll alias system and resolve the error before trying again.
|
||||
|
||||
\`${rawRollStr}\`${rawRollStr.length > 1_700 ? ' (trimmed to 2,000 characters to fit in the error message)' : ''}`.slice(0, 2_000);
|
||||
|
||||
if (!modifiers.valid) {
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Roll failed',
|
||||
description: failedRollMsg,
|
||||
fields: [
|
||||
{
|
||||
name: 'Error Details:',
|
||||
value: modifiers.error.message,
|
||||
},
|
||||
],
|
||||
footer: {
|
||||
text: modifiers.error.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('add.ts:116', newMsg, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const rollCmd = remainingArgs.join('');
|
||||
const testCmdConf = rollCmd
|
||||
.toLowerCase()
|
||||
.split(cmdSplitRegex)
|
||||
.filter((x) => x);
|
||||
try {
|
||||
assertPrePostBalance(testCmdConf, false);
|
||||
let openIdx = testCmdConf.indexOf(config.prefix);
|
||||
while (openIdx !== -1) {
|
||||
const closeIdx = getMatchingPostfixIdx(testCmdConf, openIdx, false);
|
||||
const possibleYVars = testCmdConf
|
||||
.slice(openIdx + 1, closeIdx)
|
||||
.join('')
|
||||
.split(/(y\d+(\.\d*)?)/g)
|
||||
.filter((y) => y.startsWith('y'));
|
||||
for (const yVar of possibleYVars) {
|
||||
if (yVar.includes('.')) {
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Roll failed',
|
||||
description: failedRollMsg,
|
||||
fields: [
|
||||
{
|
||||
name: 'Error Details:',
|
||||
value: `yVars cannot have decimals`,
|
||||
},
|
||||
],
|
||||
footer: {
|
||||
text: 'yVarDecimal',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('add.ts:163', newMsg, e));
|
||||
return;
|
||||
}
|
||||
if (!modifiers.yVars.has(yVar)) {
|
||||
modifiers.yVars.set(yVar, Math.ceil(Math.random() * 20));
|
||||
}
|
||||
}
|
||||
openIdx = testCmdConf.indexOf(config.prefix, closeIdx);
|
||||
}
|
||||
} catch (e) {
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Roll failed',
|
||||
description: failedRollMsg,
|
||||
fields: [
|
||||
{
|
||||
name: 'Error Details:',
|
||||
value: `Failed to find yVars, requested rollStr likely has unbalanced \`${config.prefix}\`/\`${config.postfix}\``,
|
||||
},
|
||||
{
|
||||
name: 'Raw Error:',
|
||||
value: `\`${JSON.stringify(e)}\``,
|
||||
},
|
||||
],
|
||||
footer: {
|
||||
text: 'caughtErrYVarUnbalanced',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('add.ts:191', newMsg, e));
|
||||
return;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
while (i < modifiers.yVars.size) {
|
||||
if (!modifiers.yVars.has(`y${i}`)) {
|
||||
modifiers.yVars.set(`y${i}`, 0);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
const rollStrVerdict = await new Promise<TestResults>((resolve) => {
|
||||
sendRollRequest({
|
||||
apiRoll: false,
|
||||
ddRoll: false,
|
||||
testRoll: true,
|
||||
test: { resolve },
|
||||
rollCmd,
|
||||
modifiers,
|
||||
originalCommand: rawRollStr,
|
||||
});
|
||||
});
|
||||
|
||||
if (rollStrVerdict.error) {
|
||||
const errorFields: EmbedField[] = [
|
||||
{
|
||||
name: 'Error Details:',
|
||||
value: rollStrVerdict.errorMsg,
|
||||
},
|
||||
];
|
||||
if (modifiers.yVars.size) {
|
||||
errorFields.push({
|
||||
name: 'The following YVars were used in testing:',
|
||||
value: modifiers.yVars
|
||||
.entries()
|
||||
.toArray()
|
||||
.sort((a, b) => sortYVars(a[0], b[0]))
|
||||
.map(([yVar, value]) => `\`${yVar}\`: \`${value}\``)
|
||||
.join('\n'),
|
||||
});
|
||||
}
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Roll failed',
|
||||
description: failedRollMsg,
|
||||
fields: errorFields,
|
||||
footer: {
|
||||
text: rollStrVerdict.errorCode,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('add.ts:153', newMsg, e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (replaceAlias) {
|
||||
await dbClient
|
||||
.execute('UPDATE aliases SET rollStr = ?, yVarCnt = ? WHERE guildid = ? AND userid = ? AND aliasName = ?', [
|
||||
rawRollStr,
|
||||
modifiers.yVars.size,
|
||||
guildMode ? message.guildId : 0n,
|
||||
guildMode ? 0n : message.authorId,
|
||||
aliasName.toLowerCase(),
|
||||
])
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('add.ts:169', 'update', e0);
|
||||
newMsg
|
||||
.edit(generateAliasError('DB Update Failed.', `add-q1-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:170', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
} else {
|
||||
const currentAliases = await dbClient
|
||||
.query('SELECT aliasName as count FROM aliases WHERE guildid = ? AND userid = ?', guildMode ? [message.guildId, 0n] : [0n, message.authorId])
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('add.ts:266', 'get count', e0);
|
||||
newMsg
|
||||
.edit(generateAliasError('DB Query Failed.', `add-q2-${guildMode ? 't' : 'f'}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:269', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
|
||||
if (currentAliases.length < guildMode ? config.limits.alias.free.guild : config.limits.alias.free.user) {
|
||||
await dbClient
|
||||
.execute('INSERT INTO aliases(guildid,userid,aliasName,rollStr,yVarCnt,premium) values(?,?,?,?,?,?)', [
|
||||
guildMode ? message.guildId : 0n,
|
||||
guildMode ? 0n : message.authorId,
|
||||
aliasName.toLowerCase(),
|
||||
rawRollStr,
|
||||
modifiers.yVars.size,
|
||||
0,
|
||||
])
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('add.ts:169', 'insert into', e0);
|
||||
newMsg
|
||||
.edit(generateAliasError('DB Insert Failed.', `add-q3-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:187', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
} else {
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `Over ${guildMode ? 'guild' : 'personal'} Alias Limit`,
|
||||
description: `Cannot add another alias as this account already has \`${currentAliases.length}\` aliases saved.
|
||||
|
||||
The current limits imposed on the Alias System are \`${config.limits.alias.free.guild}\` guild aliases and \`${config.limits.alias.free.user}\` account aliases.
|
||||
|
||||
If you need this limit raised, please join the [support server](${config.links.supportServer})`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageEditError('add.ts:302', newMsg, e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (errorOut) return;
|
||||
|
||||
const yVarString = ' ' + modifiers.yVars.keys().toArray().sort(sortYVars).join(' ');
|
||||
newMsg
|
||||
.edit({
|
||||
embeds: [
|
||||
{
|
||||
color: successColor,
|
||||
title: `Successfully ${replaceAlias ? 'replaced' : 'added'} the ${guildMode ? 'guild' : 'personal'} alias \`${aliasName}\`!`,
|
||||
description: `You can try it out now using the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}${aliasName}${modifiers.yVars.size ? yVarString : ''}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('add.ts:321', message, e));
|
||||
};
|
||||
|
||||
// Using wrappers to limit "magic" booleans
|
||||
export const add = (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[]) => handleAddUpdate(message, guildMode, argSpaces, false);
|
||||
export const update = (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[]) => handleAddUpdate(message, guildMode, argSpaces, true);
|
|
@ -0,0 +1,203 @@
|
|||
import { DiscordenoMessage, hasGuildPermissions } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
|
||||
import { generateAliasError } from 'embeds/alias.ts';
|
||||
import { failColor, successColor, warnColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
const handleDelete = async (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[], deleteAll: boolean) => {
|
||||
if (guildMode && !(await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR']))) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Error: Only Guild Owners and Admins can delete guild aliases',
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:16', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const verificationCode = (guildMode ? message.guildId : message.authorId).toString().slice(-4);
|
||||
const aliasName = (argSpaces.shift() || '').trim();
|
||||
argSpaces.shift();
|
||||
const userEnteredVCode = (argSpaces.shift() || '').trim();
|
||||
let errorOut = false;
|
||||
|
||||
if (!deleteAll) {
|
||||
if (!aliasName) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Error: Please specify one alias to delete',
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:38', message, e));
|
||||
return;
|
||||
} else if (!userEnteredVCode) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: warnColor,
|
||||
title: `Deletion is permanent, please confirm you want to delete \`${aliasName}\``,
|
||||
description: `Are you sure you want to delete the ${guildMode ? 'guild' : 'personal'} alias \`${aliasName}\`?
|
||||
|
||||
If you are certain you want to delete \`${aliasName}\` from ${guildMode ? 'this guild' : 'your account'}, please run the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete ${aliasName} ${verificationCode}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:54', message, e));
|
||||
return;
|
||||
} else if (userEnteredVCode !== verificationCode) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Error: Incorrect verification code',
|
||||
description: `If you are certain you want to delete \`${aliasName}\` from ${guildMode ? 'this guild' : 'your account'}, please run the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete ${aliasName} ${verificationCode}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:70', message, e));
|
||||
return;
|
||||
} else if (userEnteredVCode === verificationCode) {
|
||||
const deleteResults = await dbClient
|
||||
.execute('DELETE FROM aliases WHERE guildid = ? AND userid = ? AND aliasName = ?', [
|
||||
guildMode ? message.guildId : 0n,
|
||||
guildMode ? 0n : message.authorId,
|
||||
aliasName,
|
||||
])
|
||||
.catch((e) => {
|
||||
utils.commonLoggers.dbError('aliasDelete.ts:76', 'delete from aliases', e);
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut || !deleteResults) {
|
||||
message
|
||||
.send(generateAliasError('Delete failed.', `delete-q0-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:86', message, e));
|
||||
return;
|
||||
} else if (deleteResults.affectedRows) {
|
||||
message.send({
|
||||
embeds: [
|
||||
{
|
||||
color: successColor,
|
||||
title: 'Alias Deleted Successfully',
|
||||
description: `The ${guildMode ? 'guild' : 'personal'} alias named \`${aliasName}\` was successfully deleted.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
message.send({
|
||||
embeds: [
|
||||
{
|
||||
color: warnColor,
|
||||
title: 'Nothing deleted',
|
||||
description: `Looks like you${guildMode ? "r guild doesn't" : " don't"} have an alias named \`${aliasName}\`.
|
||||
|
||||
Please run \`${config.prefix}ra ${guildMode ? 'guild ' : ''}list\` to view the current aliases for ${guildMode ? 'this guild' : 'your account'}.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
message
|
||||
.send(generateAliasError('How are you here?', 'deleteOne-how'))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:117', message, e));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We're in deleteAll mode, so aliasName will carry the user verification code.
|
||||
// Since one wasn't provided, prompt for confirmation
|
||||
if (!aliasName) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: warnColor,
|
||||
title: 'Deletion is permanent, please confirm you want to delete all aliases',
|
||||
description: `Are you sure you want to delete all aliases for ${guildMode ? 'this guild' : 'your account'}?
|
||||
|
||||
If you are certain you want to delete all aliases for ${guildMode ? 'this guild' : 'your account'}, please run the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete-all ${verificationCode}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:137', message, e));
|
||||
return;
|
||||
} else if (aliasName !== verificationCode) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Error: Incorrect verification code',
|
||||
description: `If you are certain you want to delete all aliases for ${guildMode ? 'this guild' : 'your account'}, please run the following command:
|
||||
\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete-all ${verificationCode}\``,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:70', message, e));
|
||||
return;
|
||||
} else if (aliasName === verificationCode) {
|
||||
const deleteResults = await dbClient
|
||||
.execute('DELETE FROM aliases WHERE guildid = ? AND userid = ?', [guildMode ? message.guildId : 0n, guildMode ? 0n : message.authorId])
|
||||
.catch((e) => {
|
||||
utils.commonLoggers.dbError('aliasDelete.ts:159', 'delete from aliases', e);
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut || !deleteResults) {
|
||||
message
|
||||
.send(generateAliasError('Delete failed.', `delete-q1-${guildMode ? 't' : 'f'}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:165', message, e));
|
||||
return;
|
||||
} else if (deleteResults.affectedRows) {
|
||||
message.send({
|
||||
embeds: [
|
||||
{
|
||||
color: successColor,
|
||||
title: 'All Aliases Deleted Successfully',
|
||||
description: `All ${guildMode ? 'guild' : 'personal'} aliases for ${guildMode ? 'this guild' : 'your account'} were successfully deleted.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
message.send({
|
||||
embeds: [
|
||||
{
|
||||
color: warnColor,
|
||||
title: 'Nothing deleted',
|
||||
description: `Looks like you${guildMode ? "r guild doesn't" : " don't"} have any aliases to delete.
|
||||
|
||||
Please run \`${config.prefix}ra ${guildMode ? 'guild ' : ''}list\` to view the current aliases for ${guildMode ? 'this guild' : 'your account'}.
|
||||
If anything shows up there after running this command, please \`${config.prefix}report\` this to the developer.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
message
|
||||
.send(generateAliasError('How are you here?', 'deleteAll-how'))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('aliasDelete.ts:194', message, e));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Using wrappers to limit "magic" booleans
|
||||
export const deleteOne = (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[]) => handleDelete(message, guildMode, argSpaces, false);
|
||||
export const deleteAll = (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[]) => handleDelete(message, guildMode, argSpaces, true);
|
|
@ -0,0 +1,81 @@
|
|||
import { DiscordenoMessage } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import { ReservedWords } from 'commands/aliasCmd/reservedWords.ts';
|
||||
|
||||
import { infoColor1, infoColor2 } from 'embeds/colors.ts';
|
||||
|
||||
export const help = (message: DiscordenoMessage, guildMode: boolean) => {
|
||||
message.send({
|
||||
embeds: [
|
||||
{
|
||||
color: infoColor2,
|
||||
title: `${config.name}'s Roll Alias System Details:`,
|
||||
description: `This system allows you to save any roll string to a short, custom, memorable alias.
|
||||
|
||||
Currently, you may create up to \`${config.limits.alias.free.guild.toLocaleString()}\` per guild and \`${config.limits.alias.free.user.toLocaleString()}\` per user account. This limit may increase or decrease in the future.
|
||||
|
||||
Aliases are case-insensitive (\`tEsT\` is stored as \`test\`, but can still be called as \`tEsT\`), and are not allowed to be named any of the following: \`${
|
||||
ReservedWords.join(
|
||||
'`, `',
|
||||
)
|
||||
}\``,
|
||||
},
|
||||
{
|
||||
color: infoColor1,
|
||||
title: 'Available Alias Commands:',
|
||||
description: `- If a command has an option listed like \`help/h/?\`, this means \`help\`, \`h\`, and \`?\` are all valid options for the command.
|
||||
- \`[alias]\` indicates where you should put the desired alias to add, update, delete, or run.
|
||||
- \`[rollstr...]\` indicates where you should put the roll string for add/ing/updating an alias.
|
||||
- \`[yVars...?]\` indicates where you should put any numeric parameters needed for running the desired alias, separated by spaces. If none are needed, omit this list.
|
||||
|
||||
All commands below are shown using the shorthand version of the Roll Alias command, but \`${config.prefix}rollalias\`, \`${config.prefix}ralias\`, \`${config.prefix}alias\`, and \`${config.prefix}rolla\` also work.
|
||||
|
||||
To view ${guildMode ? '' : 'non-'}guild mode commands, please run \`${config.prefix}ra ${guildMode ? '' : 'guild '}help\``,
|
||||
fields: [
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}help/h/?\``,
|
||||
value: 'This command.',
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}add/create/set [alias] [rollstr...]\``,
|
||||
value: `Creates a new alias with the specified roll string. This is saved for use ${guildMode ? 'in only this guild' : 'by your account'}.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}list/list-all\``,
|
||||
value: `Lists all aliases and their number of yVars created ${guildMode ? 'in this guild' : 'by you'}.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}preview/view [alias]\``,
|
||||
value: `Shows the saved roll string for the specified ${guildMode ? 'guild ' : ''}alias.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}update/replace [alias] [rollstr...]\``,
|
||||
value: `Updates the specified alias to the new roll string. This overwrites the alias saved ${guildMode ? 'in this guild' : 'to your account'}.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete/remove [alias]\``,
|
||||
value: `Deletes the specified alias from ${guildMode ? 'this guild' : 'your account'}. This is a permanent deletion and cannot be undone.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}delete-all/remove-all\``,
|
||||
value: `Deletes all aliases saved to ${guildMode ? 'this guild' : 'your account'}. This is a permanent deletion and cannot be undone.`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `\`${config.prefix}ra ${guildMode ? 'guild ' : ''}[alias] [yVars...?]\` or \`${config.prefix}ra ${guildMode ? 'guild ' : ''}run/execute [alias] [yVars...?]\``,
|
||||
value: `Runs the specified ${guildMode ? 'guild ' : ''}alias with the provided yVars. yVars are only required if the alias specified requires them.`,
|
||||
inline: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
import { DiscordenoMessage } from '@discordeno';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
|
||||
import { generateAliasError } from 'embeds/alias.ts';
|
||||
import { successColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
interface QueryShape {
|
||||
aliasName: string;
|
||||
yVarCnt: number;
|
||||
}
|
||||
|
||||
export const list = async (message: DiscordenoMessage, guildMode: boolean) => {
|
||||
let errorOut = false;
|
||||
const query: QueryShape[] = await dbClient
|
||||
.query(
|
||||
`SELECT aliasName, yVarCnt FROM aliases WHERE guildid = ? AND userid = ? ORDER BY createdAt ASC`,
|
||||
guildMode ? [message.guildId, 0n] : [0n, message.authorId],
|
||||
)
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('list.ts:10', 'query', e0);
|
||||
message
|
||||
.send(generateAliasError('DB Query Failed.', `list-q0-${guildMode ? 't' : 'f'}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('list.ts:11', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: successColor,
|
||||
title: `Found ${query.length} alias${query.length === 1 ? '' : 'es'} for ${guildMode ? 'this guild' : 'your account'}:`,
|
||||
description: query.length
|
||||
? `Format shown is \`alias-name\` followed by the number of yVars required for the alias in parenthesis, if there are any required.
|
||||
|
||||
${query.map((a) => `\`${a.aliasName}\`${a.yVarCnt ? ` (${a.yVarCnt})` : ''}`).join(', ')}`
|
||||
: '',
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.messageSendError('list.ts:39', message, e0);
|
||||
message.send(generateAliasError('Message Send Failed.', `list-m0-${guildMode ? 't' : 'f'}-${guildMode ? message.guildId : message.authorId}`));
|
||||
});
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
export const ReservedWords = Object.freeze([
|
||||
'guild',
|
||||
'help',
|
||||
'h',
|
||||
'?',
|
||||
'list',
|
||||
'list-all',
|
||||
'add',
|
||||
'create',
|
||||
'set',
|
||||
'update',
|
||||
'replace',
|
||||
'preview',
|
||||
'view',
|
||||
'delete',
|
||||
'remove',
|
||||
'delete-all',
|
||||
'remove-all',
|
||||
'run',
|
||||
'execute',
|
||||
]);
|
|
@ -0,0 +1,121 @@
|
|||
import { DiscordenoMessage } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import { getModifiers } from 'artigen/dice/getModifiers.ts';
|
||||
|
||||
import { sendRollRequest } from 'artigen/managers/queueManager.ts';
|
||||
|
||||
import { generateRollError, rollingEmbed } from 'artigen/utils/embeds.ts';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
import { queries } from 'db/common.ts';
|
||||
|
||||
import { generateAliasError } from 'embeds/alias.ts';
|
||||
import { failColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
interface QueryShape {
|
||||
aliasName: string;
|
||||
yVarCnt: number;
|
||||
rollStr: string;
|
||||
}
|
||||
|
||||
export const run = async (message: DiscordenoMessage, guildMode: boolean, command: string, argSpaces: string[]) => {
|
||||
let errorOut = false;
|
||||
const aliasName = (command === 'run' || command === 'execute' ? argSpaces.shift() || '' : command)?.trim().toLowerCase();
|
||||
const yVars = new Map<string, number>();
|
||||
argSpaces
|
||||
.join('')
|
||||
.trim()
|
||||
.replaceAll('\n', ' ')
|
||||
.split(' ')
|
||||
.filter((x) => x)
|
||||
.forEach((yVar, idx) => yVars.set(`y${idx}`, parseFloat(yVar)));
|
||||
|
||||
let query: QueryShape[] = await dbClient
|
||||
.query(
|
||||
`SELECT aliasName, yVarCnt, rollStr FROM aliases WHERE guildid = ? AND userid = ? AND aliasName = ?`,
|
||||
guildMode ? [message.guildId, 0n, aliasName] : [0n, message.authorId, aliasName],
|
||||
)
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('run.ts:30', 'query', e0);
|
||||
message
|
||||
.send(generateAliasError('DB Query Failed.', `run-q0-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('run.ts:33', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
|
||||
if (!guildMode && !query.length) {
|
||||
// Didn't find an alias for the user, maybe their doing an implicit guild mode?
|
||||
query = await dbClient
|
||||
.query(`SELECT aliasName, yVarCnt, rollStr FROM aliases WHERE guildid = ? AND userid = ? AND aliasName = ?`, [message.guildId, 0n, aliasName])
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('run.ts:43', 'query', e0);
|
||||
message
|
||||
.send(generateAliasError('DB Query Failed.', `run-q1-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('run.ts:46', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
}
|
||||
|
||||
const details = query.shift();
|
||||
if (!query.length || !details) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `No alias named \`${aliasName}\` found${guildMode ? ' ' : ' on your account or '}in this guild`,
|
||||
description: `Please run \`${config.prefix}ra ${guildMode ? 'guild ' : ''}list\` to view the available aliases.`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('run.ts:63', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
if (yVars.size < details.yVarCnt) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Not enough yVars provided',
|
||||
description: `The alias \`${aliasName}\` requires \`${details.yVarCnt}\` yVars, but only \`${yVars.size}\` were provided. The roll string for this alias is:
|
||||
\`${details.rollStr}\``.slice(0, 3_000),
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('run.ts:81', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const m = await message.reply(rollingEmbed);
|
||||
|
||||
const rollStrArgSpaces = details.rollStr.split(/([ \n]+)/g);
|
||||
const [modifiers, remainingArgs] = getModifiers(rollStrArgSpaces);
|
||||
|
||||
if (!modifiers.valid) {
|
||||
m.edit(generateRollError('Modifiers invalid:', modifiers.error.name, modifiers.error.message)).catch((e) => utils.commonLoggers.messageEditError('run.ts:96', m, e));
|
||||
return;
|
||||
}
|
||||
|
||||
const currDateTime = new Date();
|
||||
dbClient.execute(queries.callIncCnt('roll')).catch((e) => utils.commonLoggers.dbError('run.ts:104', 'call sproc INC_CNT on', e));
|
||||
dbClient.execute(queries.callIncHeatmap(currDateTime)).catch((e) => utils.commonLoggers.dbError('run.ts:105', 'update', e));
|
||||
|
||||
modifiers.yVars = yVars;
|
||||
sendRollRequest({
|
||||
apiRoll: false,
|
||||
ddRoll: true,
|
||||
testRoll: false,
|
||||
dd: { myResponse: m, originalMessage: message },
|
||||
rollCmd: remainingArgs.join(''),
|
||||
modifiers,
|
||||
originalCommand: details.rollStr,
|
||||
});
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
import { DiscordenoMessage } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import dbClient from 'db/client.ts';
|
||||
|
||||
import { generateAliasError } from 'embeds/alias.ts';
|
||||
import { failColor, successColor } from 'embeds/colors.ts';
|
||||
|
||||
import utils from 'utils/utils.ts';
|
||||
|
||||
interface QueryShape {
|
||||
aliasName: string;
|
||||
yVarCnt: number;
|
||||
rollStr: string;
|
||||
}
|
||||
|
||||
export const view = async (message: DiscordenoMessage, guildMode: boolean, argSpaces: string[]) => {
|
||||
const aliasName = argSpaces.shift();
|
||||
|
||||
if (!aliasName) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'No alias provided.',
|
||||
description: `Please run this command again with an alias to search for, for example
|
||||
|
||||
If you need to see all aliases for ${guildMode ? 'this guild' : 'your account'}, please run \`${config.prefix}ra ${guildMode ? 'guild ' : ''}list\` to see all of ${
|
||||
guildMode ? "this guild's" : 'your'
|
||||
} current aliases.`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('view.ts:35', message, e));
|
||||
return;
|
||||
}
|
||||
|
||||
let errorOut = false;
|
||||
const query: QueryShape[] = await dbClient
|
||||
.query(
|
||||
`SELECT aliasName, yVarCnt, rollStr FROM aliases WHERE guildid = ? AND userid = ? AND aliasName = ?`,
|
||||
guildMode ? [message.guildId, 0n, aliasName.toLowerCase()] : [0n, message.authorId, aliasName.toLowerCase()],
|
||||
)
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.dbError('view.ts:46', 'query', e0);
|
||||
message
|
||||
.send(generateAliasError('DB Query Failed.', `view-q0-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`))
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('view.ts:49', message, e));
|
||||
errorOut = true;
|
||||
});
|
||||
if (errorOut) return;
|
||||
|
||||
const details = query[0];
|
||||
|
||||
if (details) {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: successColor,
|
||||
title: `Found the alias \`${aliasName}\` for ${guildMode ? 'this guild' : 'your account'}:`,
|
||||
description: `Y Var Count: \`${details.yVarCnt}\` Alias Name: \`${details.aliasName}\`
|
||||
${details.rollStr}`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e0) => {
|
||||
utils.commonLoggers.messageSendError('view.ts:69', message, e0);
|
||||
message.send(
|
||||
generateAliasError('Message Send Failed.', `view-m0-${guildMode ? 't' : 'f'}-${aliasName}-${guildMode ? message.guildId : message.authorId}`),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
message
|
||||
.send({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: `\`${aliasName}\` does not exist as a${guildMode ? ' guild alias' : 'n alias on your account'}.`,
|
||||
description: `Did you mean to run \`${config.prefix}ra ${guildMode ? '' : 'guild '}view ${aliasName}\`?`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((e: Error) => utils.commonLoggers.messageSendError('view.ts:85', message, e));
|
||||
}
|
||||
};
|
|
@ -19,7 +19,7 @@ const sortGuildByMemberCount = (a: DiscordenoGuild, b: DiscordenoGuild) => {
|
|||
export const auditGuilds = async (message: DiscordenoMessage) => {
|
||||
const cachedGuilds = await cacheHandlers.size('guilds');
|
||||
const guildOwnerCounts = new Map<bigint, number>();
|
||||
const sizeCats = [10000, 5000, 1000, 500, 100, 50, 25, 10, 1];
|
||||
const sizeCats = [10_000, 5_000, 1_000, 500, 100, 50, 25, 10, 1];
|
||||
const guildSizeDist = new Map<number, number>(sizeCats.map((size) => [size, 0]));
|
||||
|
||||
let totalCount = 0;
|
||||
|
|
|
@ -79,6 +79,8 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
|
|||
|
||||
sendRollRequest({
|
||||
apiRoll: false,
|
||||
ddRoll: true,
|
||||
testRoll: false,
|
||||
dd: { myResponse: m, originalMessage: message },
|
||||
rollCmd,
|
||||
modifiers,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { CreateMessage } from '@discordeno';
|
||||
|
||||
import config from '~config';
|
||||
|
||||
import { failColor } from 'embeds/colors.ts';
|
||||
|
||||
export const generateAliasError = (customMessage: string, customId: string): CreateMessage => ({
|
||||
embeds: [
|
||||
{
|
||||
color: failColor,
|
||||
title: 'Something went wrong!',
|
||||
description: `The Alias System has encountered an error:
|
||||
- ${customMessage}
|
||||
|
||||
Please try again. If this continues to happen, please \`${config.prefix}report\` the error code to the developer.`,
|
||||
footer: {
|
||||
text: `Error Code: ${customId}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
|
@ -83,7 +83,7 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
|
|||
rollCmd = rollCmd.replace(/%20/g, ' ').trim();
|
||||
|
||||
const rawSimNom = parseInt(query.get('sn') ?? '0');
|
||||
const simNom = rawSimNom || 10000;
|
||||
const simNom = rawSimNom || config.limits.defaultSimulatedNominal;
|
||||
const modifiers: RollModifiers = {
|
||||
noDetails: query.has('nd'),
|
||||
superNoDetails: query.has('snd'),
|
||||
|
@ -102,6 +102,7 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
|
|||
rollDist: query.has('rd'),
|
||||
numberVariables: query.has('nv') || query.has('vn'),
|
||||
customDiceShapes: new Map<string, number[]>(),
|
||||
yVars: new Map<string, number>(),
|
||||
apiWarn: hideWarn ? '' : apiWarning,
|
||||
valid: true,
|
||||
error: new Error(),
|
||||
|
@ -154,13 +155,15 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
|
|||
}
|
||||
|
||||
// simulatedNominal cannot be greater than config.limits.simulatedNominal
|
||||
if (modifiers.simulatedNominal > config.limits.simulatedNominal) {
|
||||
return stdResp.BadRequest(`Number of iterations for \`simulatedNominal\` cannot be greater than \`${config.limits.simulatedNominal}\``);
|
||||
if (modifiers.simulatedNominal > config.limits.maxSimulatedNominal) {
|
||||
return stdResp.BadRequest(`Number of iterations for \`simulatedNominal\` cannot be greater than \`${config.limits.maxSimulatedNominal}\``);
|
||||
}
|
||||
|
||||
return new Promise<Response>((resolve) => {
|
||||
sendRollRequest({
|
||||
apiRoll: true,
|
||||
ddRoll: false,
|
||||
testRoll: false,
|
||||
api: { resolve, channelId: BigInt(query.get('channel') || '0'), userId: BigInt(query.get('user') || '') },
|
||||
rollCmd,
|
||||
modifiers,
|
||||
|
|
|
@ -26,7 +26,7 @@ const sendGuildJoinedBatch = () => {
|
|||
|
||||
setInterval(() => {
|
||||
sendGuildJoinedBatch();
|
||||
}, 60 * 1000);
|
||||
}, 60_000);
|
||||
|
||||
export const guildCreateHandler = (guild: DiscordenoGuild) => {
|
||||
log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`);
|
||||
|
|
|
@ -135,6 +135,15 @@ export const messageCreateHandler = (message: DiscordenoMessage) => {
|
|||
// Enable or Disable inline rolling
|
||||
commands.toggleInline(message, args);
|
||||
break;
|
||||
case 'rollalias':
|
||||
case 'ralias':
|
||||
case 'alias':
|
||||
case 'rolla':
|
||||
case 'ra':
|
||||
// [[rollalias, [[ralias, [[rolla, or [[ra args
|
||||
// Manage and roll using aliases
|
||||
commands.alias(message, argSpaces);
|
||||
break;
|
||||
case 'roll':
|
||||
case 'r':
|
||||
// [[roll or [[r
|
||||
|
|
|
@ -40,25 +40,25 @@ export const readyHandler = () => {
|
|||
} catch (e) {
|
||||
log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`);
|
||||
}
|
||||
}, 30000);
|
||||
}, 30_000);
|
||||
|
||||
// Interval to update bot list stats every 24 hours
|
||||
LOCALMODE ? log(LT.INFO, 'updateListStatistics not running') : setInterval(() => {
|
||||
log(LT.LOG, 'Updating all bot lists statistics');
|
||||
intervals.updateListStatistics(botId, cache.guilds.size + cache.dispatchedGuildIds.size);
|
||||
}, 86400000);
|
||||
}, 86_400_000);
|
||||
|
||||
// Interval to update hourlyRates every hour
|
||||
setInterval(() => {
|
||||
log(LT.LOG, 'Updating all command hourlyRates');
|
||||
intervals.updateHourlyRates();
|
||||
}, 3600000);
|
||||
}, 3_600_000);
|
||||
|
||||
// Interval to update heatmap.png every hour
|
||||
setInterval(() => {
|
||||
log(LT.LOG, 'Updating heatmap.png');
|
||||
intervals.updateHeatmapPng();
|
||||
}, 3600000);
|
||||
}, 3_600_000);
|
||||
|
||||
// setTimeout added to make sure the startup message does not error out
|
||||
setTimeout(() => {
|
||||
|
@ -91,5 +91,5 @@ export const readyHandler = () => {
|
|||
},
|
||||
],
|
||||
}).catch((e: Error) => utils.commonLoggers.messageSendError('ready.ts:93', 'Startup', e));
|
||||
}, 1000);
|
||||
}, 1_000);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue