1
1
mirror of https://github.com/Burn-E99/TheArtificer.git synced 2026-06-04 09:03:50 -04:00

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:
Ean Milligan
2025-07-13 01:22:47 -04:00
parent fedba62d52
commit 4bbdb59f3d
32 changed files with 1358 additions and 169 deletions

View File

@@ -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,

81
src/commands/aliasCmd.ts Normal file
View File

@@ -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;
}
};

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,
},
],
},
],
});
};

View File

@@ -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}`));
});
};

View File

@@ -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',
]);

View File

@@ -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,
});
};

View File

@@ -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));
}
};

View File

@@ -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;

View File

@@ -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,