added deno.json, ran deno fmt to standardize formatting

This commit is contained in:
Ean Milligan (Bastion) 2022-05-20 04:43:22 -04:00
parent 925eb08205
commit 5e84b1c0b4
39 changed files with 1323 additions and 1219 deletions

View File

@ -1,25 +1,25 @@
// This file will create all tables for the artificer schema // This file will create all tables for the artificer schema
// DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK // DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK
import config from "../config.ts"; import config from '../config.ts';
import { dbClient } from "../src/db.ts"; import { dbClient } from '../src/db.ts';
console.log("Attempting to create DB"); console.log('Attempting to create DB');
await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`); await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`);
await dbClient.execute(`USE ${config.db.name}`); await dbClient.execute(`USE ${config.db.name}`);
console.log("DB created"); console.log('DB created');
console.log("Attempt to drop all tables"); console.log('Attempt to drop all tables');
await dbClient.execute(`DROP TABLE IF EXISTS allowed_channels;`); await dbClient.execute(`DROP TABLE IF EXISTS allowed_channels;`);
await dbClient.execute(`DROP TABLE IF EXISTS all_keys;`); await dbClient.execute(`DROP TABLE IF EXISTS all_keys;`);
await dbClient.execute(`DROP TABLE IF EXISTS allowed_guilds;`); await dbClient.execute(`DROP TABLE IF EXISTS allowed_guilds;`);
await dbClient.execute(`DROP TABLE IF EXISTS roll_log;`); await dbClient.execute(`DROP TABLE IF EXISTS roll_log;`);
await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`); 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 command_cnt;`);
console.log("Tables dropped"); console.log('Tables dropped');
// Light telemetry on how many commands have been run // Light telemetry on how many commands have been run
console.log("Attempting to create table command_cnt"); console.log('Attempting to create table command_cnt');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE command_cnt ( CREATE TABLE command_cnt (
command char(20) NOT NULL, command char(20) NOT NULL,
@ -28,9 +28,9 @@ await dbClient.execute(`
UNIQUE KEY command_cnt_command_UNIQUE (command) UNIQUE KEY command_cnt_command_UNIQUE (command)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log("Table created"); console.log('Table created');
console.log("Attempt creating increment Stored Procedure"); console.log('Attempt creating increment Stored Procedure');
await dbClient.execute(` await dbClient.execute(`
CREATE PROCEDURE INC_CNT( CREATE PROCEDURE INC_CNT(
IN cmd CHAR(20) IN cmd CHAR(20)
@ -41,10 +41,10 @@ await dbClient.execute(`
UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd; UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd;
END END
`); `);
console.log("Stored Procedure created"); console.log('Stored Procedure created');
// Roll log, holds rolls when requests // Roll log, holds rolls when requests
console.log("Attempting to create table roll_log"); console.log('Attempting to create table roll_log');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE roll_log ( CREATE TABLE roll_log (
id int unsigned NOT NULL AUTO_INCREMENT, id int unsigned NOT NULL AUTO_INCREMENT,
@ -59,10 +59,10 @@ await dbClient.execute(`
UNIQUE KEY roll_log_resultid_UNIQUE (resultid) UNIQUE KEY roll_log_resultid_UNIQUE (resultid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log("Table created"); console.log('Table created');
// Api guild settings // Api guild settings
console.log("Attempting to create table allowed_guilds"); console.log('Attempting to create table allowed_guilds');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE allowed_guilds ( CREATE TABLE allowed_guilds (
guildid bigint unsigned NOT NULL, guildid bigint unsigned NOT NULL,
@ -74,10 +74,10 @@ await dbClient.execute(`
PRIMARY KEY (guildid, channelid) PRIMARY KEY (guildid, channelid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log("Table created"); console.log('Table created');
// Api keys // Api keys
console.log("Attempting to create table all_keys"); console.log('Attempting to create table all_keys');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE all_keys ( CREATE TABLE all_keys (
userid bigint unsigned NOT NULL, userid bigint unsigned NOT NULL,
@ -93,10 +93,10 @@ await dbClient.execute(`
UNIQUE KEY all_keys_email_UNIQUE (email) UNIQUE KEY all_keys_email_UNIQUE (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log("Table created"); console.log('Table created');
// Api user settings // Api user settings
console.log("Attempting to create table allowed_channels"); console.log('Attempting to create table allowed_channels');
await dbClient.execute(` await dbClient.execute(`
CREATE TABLE allowed_channels ( CREATE TABLE allowed_channels (
userid bigint unsigned NOT NULL, userid bigint unsigned NOT NULL,
@ -108,7 +108,7 @@ await dbClient.execute(`
CONSTRAINT allowed_channels_userid_FK FOREIGN KEY (userid) REFERENCES all_keys (userid) ON DELETE RESTRICT ON UPDATE RESTRICT CONSTRAINT allowed_channels_userid_FK FOREIGN KEY (userid) REFERENCES all_keys (userid) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`); `);
console.log("Table created"); console.log('Table created');
await dbClient.close(); await dbClient.close();
console.log("Done!"); console.log('Done!');

View File

@ -1,22 +1,22 @@
// This file will populate the tables with default values // This file will populate the tables with default values
import config from "../config.ts"; import config from '../config.ts';
import { dbClient } from "../src/db.ts"; import { dbClient } from '../src/db.ts';
console.log("Attempting to populate DB Admin API key"); console.log('Attempting to populate DB Admin API key');
await dbClient.execute("INSERT INTO all_keys(userid,apiKey) values(?,?)", [config.api.admin, config.api.adminKey]).catch(e => { await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [config.api.admin, config.api.adminKey]).catch((e) => {
console.log("Failed to insert into database", e); console.log('Failed to insert into database', e);
}); });
console.log("Inesrtion done"); console.log('Inesrtion done');
console.log("Attempting to insert default commands into command_cnt"); console.log('Attempting to insert default commands into command_cnt');
const commands = ["ping", "rip", "rollhelp", "help", "info", "version", "report", "stats", "roll", "emojis", "api", "privacy", "mention"]; const commands = ['ping', 'rip', 'rollhelp', 'help', 'info', 'version', 'report', 'stats', 'roll', 'emojis', 'api', 'privacy', 'mention'];
for (let i = 0; i < commands.length; i++) { for (let i = 0; i < commands.length; i++) {
await dbClient.execute("INSERT INTO command_cnt(command) values(?)", [commands[i]]).catch(e => { await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [commands[i]]).catch((e) => {
console.log(`Failed to insert into database`, e); console.log(`Failed to insert into database`, e);
}); });
} }
console.log("Insertion done"); console.log('Insertion done');
await dbClient.close(); await dbClient.close();
console.log("Done!"); console.log('Done!');

31
deno.json Normal file
View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"allowJs": true,
"lib": ["deno.window"],
"strict": true
},
"lint": {
"files": {
"include": ["src/", "db/", "mod.ts"],
"exclude": []
},
"rules": {
"tags": ["recommended"],
"include": ["ban-untagged-todo"],
"exclude": []
}
},
"fmt": {
"files": {
"include": ["src/", "db/", "mod.ts"],
"exclude": []
},
"options": {
"useTabs": true,
"lineWidth": 200,
"indentWidth": 2,
"singleQuote": true,
"proseWrap": "preserve"
}
}
}

142
mod.ts
View File

@ -4,26 +4,32 @@
* December 21, 2020 * December 21, 2020
*/ */
import config from "./config.ts"; import config from './config.ts';
import { DEBUG, DEVMODE, LOCALMODE } from "./flags.ts"; import { DEBUG, DEVMODE, LOCALMODE } from './flags.ts';
import { import {
// Discordeno deps botId,
startBot, editBotStatus, editBotNickname, cache,
DiscordActivityTypes,
DiscordenoGuild,
DiscordenoMessage,
editBotNickname,
editBotStatus,
initLog,
Intents, Intents,
sendMessage, log,
cache, botId,
DiscordActivityTypes, DiscordenoGuild, DiscordenoMessage,
// Log4Deno deps // Log4Deno deps
LT, initLog, log LT,
} from "./deps.ts"; sendMessage,
import api from "./src/api.ts"; // Discordeno deps
import commands from "./src/commands/_index.ts"; startBot,
import intervals from "./src/intervals.ts"; } from './deps.ts';
import utils from "./src/utils.ts"; import api from './src/api.ts';
import commands from './src/commands/_index.ts';
import intervals from './src/intervals.ts';
import utils from './src/utils.ts';
// Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup // Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup
initLog("logs", DEBUG); initLog('logs', DEBUG);
// Start up the Discord Bot // Start up the Discord Bot
startBot({ startBot({
@ -34,25 +40,25 @@ startBot({
log(LT.INFO, `${config.name} Logged in!`); log(LT.INFO, `${config.name} Logged in!`);
editBotStatus({ editBotStatus({
activities: [{ activities: [{
name: "Booting up . . .", name: 'Booting up . . .',
type: DiscordActivityTypes.Game, type: DiscordActivityTypes.Game,
createdAt: new Date().getTime() createdAt: new Date().getTime(),
}], }],
status: "online" status: 'online',
}); });
// Interval to rotate the status text every 30 seconds to show off more commands // Interval to rotate the status text every 30 seconds to show off more commands
setInterval(async () => { setInterval(async () => {
log(LT.LOG, "Changing bot status"); log(LT.LOG, 'Changing bot status');
try { try {
// Wrapped in try-catch due to hard crash possible // Wrapped in try-catch due to hard crash possible
editBotStatus({ editBotStatus({
activities: [{ activities: [{
name: await intervals.getRandomStatus(), name: await intervals.getRandomStatus(),
type: DiscordActivityTypes.Game, type: DiscordActivityTypes.Game,
createdAt: new Date().getTime() createdAt: new Date().getTime(),
}], }],
status: "online" status: 'online',
}); });
} catch (e) { } catch (e) {
log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`);
@ -60,45 +66,45 @@ startBot({
}, 30000); }, 30000);
// Interval to update bot list stats every 24 hours // Interval to update bot list stats every 24 hours
LOCALMODE ? log(LT.INFO, "updateListStatistics not running") : setInterval(() => { LOCALMODE ? log(LT.INFO, 'updateListStatistics not running') : setInterval(() => {
log(LT.LOG, "Updating all bot lists statistics"); log(LT.LOG, 'Updating all bot lists statistics');
intervals.updateListStatistics(botId, cache.guilds.size); intervals.updateListStatistics(botId, cache.guilds.size);
}, 86400000); }, 86400000);
// setTimeout added to make sure the startup message does not error out // setTimeout added to make sure the startup message does not error out
setTimeout(() => { setTimeout(() => {
LOCALMODE && editBotNickname(config.devServer, `LOCAL - ${config.name}`); LOCALMODE && editBotNickname(config.devServer, `LOCAL - ${config.name}`);
LOCALMODE ? log(LT.INFO, "updateListStatistics not running") : intervals.updateListStatistics(botId, cache.guilds.size); LOCALMODE ? log(LT.INFO, 'updateListStatistics not running') : intervals.updateListStatistics(botId, cache.guilds.size);
editBotStatus({ editBotStatus({
activities: [{ activities: [{
name: "Booting Complete", name: 'Booting Complete',
type: DiscordActivityTypes.Game, type: DiscordActivityTypes.Game,
createdAt: new Date().getTime() createdAt: new Date().getTime(),
}], }],
status: "online" status: 'online',
}); });
sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch(e => { sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
}); });
}, 1000); }, 1000);
}, },
guildCreate: (guild: DiscordenoGuild) => { guildCreate: (guild: DiscordenoGuild) => {
log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`); log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`);
sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch(e => { sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
}); });
}, },
guildDelete: (guild: DiscordenoGuild) => { guildDelete: (guild: DiscordenoGuild) => {
log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`); log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`);
sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch(e => { sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`);
}); });
}, },
debug: DEVMODE ? dmsg => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : () => {}, debug: DEVMODE ? (dmsg) => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : () => {},
messageCreate: (message: DiscordenoMessage) => { messageCreate: (message: DiscordenoMessage) => {
// Ignore all other bots // Ignore all other bots
if (message.isBot) return; if (message.isBot) return;
// Ignore all messages that are not commands // Ignore all messages that are not commands
if (message.content.indexOf(config.prefix) !== 0) { if (message.content.indexOf(config.prefix) !== 0) {
// Handle @bot messages // Handle @bot messages
@ -109,7 +115,7 @@ startBot({
// return as we are done handling this command // return as we are done handling this command
return; return;
} }
log(LT.LOG, `Handling ${config.prefix}command message: ${JSON.stringify(message)}`); log(LT.LOG, `Handling ${config.prefix}command message: ${JSON.stringify(message)}`);
// Split into standard command + args format // Split into standard command + args format
@ -120,77 +126,55 @@ startBot({
// [[ping // [[ping
// Its a ping test, what else do you want. // Its a ping test, what else do you want.
if (command === "ping") { if (command === 'ping') {
commands.ping(message); commands.ping(message);
} } // [[rip [[memory
// [[rip [[memory
// Displays a short message I wanted to include // Displays a short message I wanted to include
else if (command === "rip" || command === "memory") { else if (command === 'rip' || command === 'memory') {
commands.rip(message); commands.rip(message);
} } // [[rollhelp or [[rh or [[hr or [[??
// [[rollhelp or [[rh or [[hr or [[??
// Help command specifically for the roll command // Help command specifically for the roll command
else if (command === "rollhelp" || command === "rh" || command === "hr" || command === "??" || command?.startsWith("xdy")) { else if (command === 'rollhelp' || command === 'rh' || command === 'hr' || command === '??' || command?.startsWith('xdy')) {
commands.rollHelp(message); commands.rollHelp(message);
} } // [[help or [[h or [[?
// [[help or [[h or [[?
// Help command, prints from help file // Help command, prints from help file
else if (command === "help" || command === "h" || command === "?") { else if (command === 'help' || command === 'h' || command === '?') {
commands.help(message); commands.help(message);
} } // [[info or [[i
// [[info or [[i
// Info command, prints short desc on bot and some links // Info command, prints short desc on bot and some links
else if (command === "info" || command === "i") { else if (command === 'info' || command === 'i') {
commands.info(message); commands.info(message);
} } // [[privacy
// [[privacy
// Privacy command, prints short desc on bot's privacy policy // Privacy command, prints short desc on bot's privacy policy
else if (command === "privacy") { else if (command === 'privacy') {
commands.privacy(message); commands.privacy(message);
} } // [[version or [[v
// [[version or [[v
// Returns version of the bot // Returns version of the bot
else if (command === "version" || command === "v") { else if (command === 'version' || command === 'v') {
commands.version(message); commands.version(message);
} } // [[report or [[r (command that failed)
// [[report or [[r (command that failed)
// Manually report a failed roll // Manually report a failed roll
else if (command === "report" || command === "r") { else if (command === 'report' || command === 'r') {
commands.report(message, args); commands.report(message, args);
} } // [[stats or [[s
// [[stats or [[s
// Displays stats on the bot // Displays stats on the bot
else if (command === "stats" || command === "s") { else if (command === 'stats' || command === 's') {
commands.stats(message); commands.stats(message);
} } // [[api arg
// [[api arg
// API sub commands // API sub commands
else if (command === "api") { else if (command === 'api') {
commands.api(message, args); commands.api(message, args);
} } // [[roll]]
// [[roll]]
// Dice rolling commence! // Dice rolling commence!
else if (command && (`${command}${args.join("")}`).indexOf(config.postfix) > -1) { else if (command && (`${command}${args.join('')}`).indexOf(config.postfix) > -1) {
commands.roll(message, args, command); commands.roll(message, args, command);
} } // [[emoji or [[emojialias
// [[emoji or [[emojialias
// Check if the unhandled command is an emoji request // Check if the unhandled command is an emoji request
else if (command) { else if (command) {
commands.emoji(message, command); commands.emoji(message, command);
} }
} },
} },
}); });
// Start up the command prompt for debug usage // Start up the command prompt for debug usage

View File

@ -6,27 +6,26 @@
import { import {
// Discordeno deps // Discordeno deps
cache, CreateMessage, cache,
sendMessage, sendDirectMessage, CreateMessage,
log,
// httpd deps // Log4Deno deps
Status, STATUS_TEXT, LT,
// nanoid deps // nanoid deps
nanoid, nanoid,
sendDirectMessage,
sendMessage,
// httpd deps
Status,
STATUS_TEXT,
} from '../deps.ts';
// Log4Deno deps import { RollModifiers } from './mod.d.ts';
LT, log import { dbClient, queries } from './db.ts';
} from "../deps.ts"; import solver from './solver/_index.ts';
import { generateApiDeleteEmail, generateApiKeyEmail, generateDMFailed } from './constantCmds.ts';
import { RollModifiers } from "./mod.d.ts"; import config from '../config.ts';
import { dbClient, queries } from "./db.ts";
import solver from "./solver/_index.ts";
import {
generateApiKeyEmail, generateApiDeleteEmail, generateDMFailed
} from "./constantCmds.ts";
import config from "../config.ts";
// start(databaseClient) returns nothing // start(databaseClient) returns nothing
// start initializes and runs the entire API for the bot // start initializes and runs the entire API for the bot
@ -51,14 +50,14 @@ const start = async (): Promise<void> => {
let rateLimited = false; let rateLimited = false;
let updateRateLimitTime = false; let updateRateLimitTime = false;
let apiUserid = 0n; let apiUserid = 0n;
let apiUseridStr = ""; let apiUseridStr = '';
let apiUserEmail = ""; let apiUserEmail = '';
let apiUserDelCode = ""; let apiUserDelCode = '';
// Check the requests API key // Check the requests API key
if (request.headers.has("X-Api-Key")) { if (request.headers.has('X-Api-Key')) {
// Get the userid and flags for the specific key // Get the userid and flags for the specific key
const dbApiQuery = await dbClient.query("SELECT userid, email, deleteCode FROM all_keys WHERE apiKey = ? AND active = 1 AND banned = 0", [request.headers.get("X-Api-Key")]); const dbApiQuery = await dbClient.query('SELECT userid, email, deleteCode FROM all_keys WHERE apiKey = ? AND active = 1 AND banned = 0', [request.headers.get('X-Api-Key')]);
// If only one user returned, is not banned, and is currently active, mark as authenticated // If only one user returned, is not banned, and is currently active, mark as authenticated
if (dbApiQuery.length === 1) { if (dbApiQuery.length === 1) {
@ -77,7 +76,7 @@ const start = async (): Promise<void> => {
const currentCnt = rateLimitCnt.get(apiUseridStr) || 0; const currentCnt = rateLimitCnt.get(apiUseridStr) || 0;
if (currentCnt < config.api.rateLimitCnt) { if (currentCnt < config.api.rateLimitCnt) {
// Limit not yet exceeded, update count // Limit not yet exceeded, update count
rateLimitCnt.set(apiUseridStr, (currentCnt + 1)); rateLimitCnt.set(apiUseridStr, currentCnt + 1);
} else { } else {
// Limit exceeded, prevent API use // Limit exceeded, prevent API use
rateLimited = true; rateLimited = true;
@ -92,26 +91,26 @@ const start = async (): Promise<void> => {
if (authenticated && !rateLimited) { if (authenticated && !rateLimited) {
// Get path and query as a string // Get path and query as a string
const [path, tempQ] = request.url.split("?"); const [path, tempQ] = request.url.split('?');
// Turn the query into a map (if it exists) // Turn the query into a map (if it exists)
const query = new Map<string, string>(); const query = new Map<string, string>();
if (tempQ !== undefined) { if (tempQ !== undefined) {
tempQ.split("&").forEach((e: string) => { tempQ.split('&').forEach((e: string) => {
log(LT.LOG, `Breaking down request query: ${request} ${e}`); log(LT.LOG, `Breaking down request query: ${request} ${e}`);
const [option, params] = e.split("="); const [option, params] = e.split('=');
query.set(option.toLowerCase(), params); query.set(option.toLowerCase(), params);
}); });
} }
// Handle the request // Handle the request
switch (request.method) { switch (request.method) {
case "GET": case 'GET':
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case "/api/key": case '/api/key':
case "/api/key/": case '/api/key/':
if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("a") && ((query.get("a") || "").length > 0))) { if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('a') && ((query.get('a') || '').length > 0))) {
if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) {
// Generate new secure key // Generate new secure key
const newKey = await nanoid(25); const newKey = await nanoid(25);
@ -119,7 +118,7 @@ const start = async (): Promise<void> => {
let erroredOut = false; let erroredOut = false;
// Insert new key/user pair into the db // Insert new key/user pair into the db
await dbClient.execute("INSERT INTO all_keys(userid,apiKey) values(?,?)", [apiUserid, newKey]).catch(e => { await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [apiUserid, newKey]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -130,7 +129,7 @@ const start = async (): Promise<void> => {
break; break;
} else { } else {
// Send API key as response // Send API key as response
requestEvent.respondWith(new Response(JSON.stringify({ "key": newKey, "userid": query.get("user") }), { status: Status.OK })); requestEvent.respondWith(new Response(JSON.stringify({ 'key': newKey, 'userid': query.get('user') }), { status: Status.OK }));
break; break;
} }
} else { } else {
@ -142,15 +141,15 @@ const start = async (): Promise<void> => {
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
} }
break; break;
case "/api/channel": case '/api/channel':
case "/api/channel/": case '/api/channel/':
if (query.has("user") && ((query.get("user") || "").length > 0)) { if (query.has('user') && ((query.get('user') || '').length > 0)) {
if (apiUserid === BigInt(query.get("user") || "0")) { if (apiUserid === BigInt(query.get('user') || '0')) {
// Flag to see if there is an error inside the catch // Flag to see if there is an error inside the catch
let erroredOut = false; let erroredOut = false;
// Get all channels userid has authorized // Get all channels userid has authorized
const dbAllowedChannelQuery = await dbClient.query("SELECT * FROM allowed_channels WHERE userid = ?", [apiUserid]).catch(e => { const dbAllowedChannelQuery = await dbClient.query('SELECT * FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -174,11 +173,14 @@ const start = async (): Promise<void> => {
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
} }
break; break;
case "/api/roll": case '/api/roll':
case "/api/roll/": case '/api/roll/':
// Make sure query contains all the needed parts // Make sure query contains all the needed parts
if ((query.has("rollstr") && ((query.get("rollstr") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { if (
if (query.has("n") && query.has("m")) { (query.has('rollstr') && ((query.get('rollstr') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0)) &&
(query.has('user') && ((query.get('user') || '').length > 0))
) {
if (query.has('n') && query.has('m')) {
// Alert API user that they shouldn't be doing this // Alert API user that they shouldn't be doing this
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
break; break;
@ -189,13 +191,15 @@ const start = async (): Promise<void> => {
let hideWarn = false; let hideWarn = false;
// Check if the db has the requested userid/channelid combo, and that the requested userid matches the userid linked with the api key // Check if the db has the requested userid/channelid combo, and that the requested userid matches the userid linked with the api key
const dbChannelQuery = await dbClient.query("SELECT active, banned FROM allowed_channels WHERE userid = ? AND channelid = ?", [apiUserid, BigInt(query.get("channel") || "0")]); const dbChannelQuery = await dbClient.query('SELECT active, banned FROM allowed_channels WHERE userid = ? AND channelid = ?', [apiUserid, BigInt(query.get('channel') || '0')]);
if (dbChannelQuery.length === 1 && (apiUserid === BigInt(query.get("user") || "0")) && dbChannelQuery[0].active && !dbChannelQuery[0].banned) { if (dbChannelQuery.length === 1 && (apiUserid === BigInt(query.get('user') || '0')) && dbChannelQuery[0].active && !dbChannelQuery[0].banned) {
// Get the guild from the channel and make sure user is in said guild // Get the guild from the channel and make sure user is in said guild
const guild = cache.channels.get(BigInt(query.get("channel") || ""))?.guild; const guild = cache.channels.get(BigInt(query.get('channel') || ''))?.guild;
if (guild && guild.members.get(BigInt(query.get("user") || ""))?.id) { if (guild && guild.members.get(BigInt(query.get('user') || ''))?.id) {
const dbGuildQuery = await dbClient.query("SELECT active, banned, hidewarn FROM allowed_guilds WHERE guildid = ? AND channelid = ?", [guild.id, BigInt(query.get("channel") || "0")]); const dbGuildQuery = await dbClient.query('SELECT active, banned, hidewarn FROM allowed_guilds WHERE guildid = ? AND channelid = ?', [
guild.id,
BigInt(query.get('channel') || '0'),
]);
// Make sure guild allows API rolls // Make sure guild allows API rolls
if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) { if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) {
@ -211,76 +215,78 @@ const start = async (): Promise<void> => {
// Flag to tell if roll was completely successful // Flag to tell if roll was completely successful
let errorOut = false; let errorOut = false;
// Make sure rollCmd is not undefined // Make sure rollCmd is not undefined
let rollCmd = query.get("rollstr") || ""; let rollCmd = query.get('rollstr') || '';
const originalCommand = query.get("rollstr"); const originalCommand = query.get('rollstr');
if (rollCmd.length === 0) { if (rollCmd.length === 0) {
// Alert API user that they messed up // Alert API user that they messed up
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
// Always log API rolls for abuse detection // Always log API rolls for abuse detection
dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "EmptyInput", null]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'EmptyInput', null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
break; break;
} }
if (query.has("o") && (query.get("o")?.toLowerCase() !== "d" && query.get("o")?.toLowerCase() !== "a")) { if (query.has('o') && (query.get('o')?.toLowerCase() !== 'd' && query.get('o')?.toLowerCase() !== 'a')) {
// Alert API user that they messed up // Alert API user that they messed up
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
// Always log API rolls for abuse detection // Always log API rolls for abuse detection
dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "BadOrder", null]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'BadOrder', null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
break; break;
} }
// Clip off the leading prefix. API calls must be formatted with a prefix at the start to match how commands are sent in Discord // Clip off the leading prefix. API calls must be formatted with a prefix at the start to match how commands are sent in Discord
rollCmd = rollCmd.substring(rollCmd.indexOf(config.prefix) + 2).replace(/%20/g, " "); rollCmd = rollCmd.substring(rollCmd.indexOf(config.prefix) + 2).replace(/%20/g, ' ');
const modifiers: RollModifiers = { const modifiers: RollModifiers = {
noDetails: false, noDetails: false,
superNoDetails: false, superNoDetails: false,
spoiler: "", spoiler: '',
maxRoll: query.has("m"), maxRoll: query.has('m'),
nominalRoll: query.has("n"), nominalRoll: query.has('n'),
gmRoll: false, gmRoll: false,
gms: [], gms: [],
order: query.has("o") ? (query.get("o")?.toLowerCase() || "") : "", order: query.has('o') ? (query.get('o')?.toLowerCase() || '') : '',
valid: true, valid: true,
count: query.has("c") count: query.has('c'),
}; };
// Parse the roll and get the return text // Parse the roll and get the return text
const returnmsg = solver.parseRoll(rollCmd, modifiers); const returnmsg = solver.parseRoll(rollCmd, modifiers);
// Alert users why this message just appeared and how they can report abues pf this feature // Alert users why this message just appeared and how they can report abues pf this feature
const apiPrefix = hideWarn ? '' : `The following roll was conducted using my built in API. If someone in this channel did not request this roll, please report API abuse here: <${config.api.supportURL}>\n\n`; const apiPrefix = hideWarn
let m, returnText = ""; ? ''
: `The following roll was conducted using my built in API. If someone in this channel did not request this roll, please report API abuse here: <${config.api.supportURL}>\n\n`;
let m, returnText = '';
// Handle sending the error message to whoever called the api // Handle sending the error message to whoever called the api
if (returnmsg.error) { if (returnmsg.error) {
requestEvent.respondWith(new Response(returnmsg.errorMsg, { status: Status.InternalServerError })); requestEvent.respondWith(new Response(returnmsg.errorMsg, { status: Status.InternalServerError }));
// Always log API rolls for abuse detection // Always log API rolls for abuse detection
dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, returnmsg.errorCode, null]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, returnmsg.errorCode, null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
break; break;
} else { } else {
returnText = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}`; returnText = `${apiPrefix}<@${query.get('user')}>${returnmsg.line1}\n${returnmsg.line2}`;
let spoilerTxt = ""; let spoilerTxt = '';
// Determine if spoiler flag was on // Determine if spoiler flag was on
if (query.has("s")) { if (query.has('s')) {
spoilerTxt = "||"; spoilerTxt = '||';
} }
// Determine if no details flag was on // Determine if no details flag was on
if (!query.has("snd")) { if (!query.has('snd')) {
if (query.has("nd")) { if (query.has('nd')) {
returnText += "\nDetails suppressed by nd query."; returnText += '\nDetails suppressed by nd query.';
} else { } else {
returnText += `\nDetails:\n${spoilerTxt}${returnmsg.line3}${spoilerTxt}`; returnText += `\nDetails:\n${spoilerTxt}${returnmsg.line3}${spoilerTxt}`;
} }
@ -288,70 +294,74 @@ const start = async (): Promise<void> => {
} }
// If the roll was a GM roll, send DMs to all the GMs // If the roll was a GM roll, send DMs to all the GMs
if (query.has("gms")) { if (query.has('gms')) {
// Get all the GM user IDs from the query // Get all the GM user IDs from the query
const gms = (query.get("gms") || "").split(","); const gms = (query.get('gms') || '').split(',');
if (gms.length === 0) { if (gms.length === 0) {
// Alert API user that they messed up // Alert API user that they messed up
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
// Always log API rolls for abuse detection // Always log API rolls for abuse detection
dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "NoGMsSent", null]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'NoGMsSent', null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
break; break;
} }
// Make a new return line to be sent to the roller // Make a new return line to be sent to the roller
let normalText = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\nResults have been messaged to the following GMs: `; let normalText = `${apiPrefix}<@${query.get('user')}>${returnmsg.line1}\nResults have been messaged to the following GMs: `;
gms.forEach(e => { gms.forEach((e) => {
log(LT.LOG, `Appending GM ${e} to roll text`); log(LT.LOG, `Appending GM ${e} to roll text`);
normalText += `<@${e}> `; normalText += `<@${e}> `;
}); });
// Send the return message as a DM or normal message depening on if the channel is set // Send the return message as a DM or normal message depening on if the channel is set
if ((query.get("channel") || "").length > 0) { if ((query.get('channel') || '').length > 0) {
// todo: embedify // todo: embedify
m = await sendMessage(BigInt(query.get("channel") || ""), normalText).catch(() => { m = await sendMessage(BigInt(query.get('channel') || ''), normalText).catch(() => {
requestEvent.respondWith(new Response("Message 00 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 00 failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} else { } else {
// todo: embedify // todo: embedify
m = await sendDirectMessage(BigInt(query.get("user") || ""), normalText).catch(() => { m = await sendDirectMessage(BigInt(query.get('user') || ''), normalText).catch(() => {
requestEvent.respondWith(new Response("Message 01 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 01 failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} }
const newMessage: CreateMessage = {}; const newMessage: CreateMessage = {};
// If its too big, collapse it into a .txt file and send that instead. // If its too big, collapse it into a .txt file and send that instead.
const b = await new Blob([returnText as BlobPart], { "type": "text" }); const b = await new Blob([returnText as BlobPart], { 'type': 'text' });
if (b.size > 8388290) { if (b.size > 8388290) {
// Update return text // Update return text
newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; newMessage.content = `${apiPrefix}<@${
query.get('user')
}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`;
} else { } else {
// Update return text // Update return text
newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`; newMessage.content = `${apiPrefix}<@${
newMessage.file = { "blob": b, "name": "rollDetails.txt" }; query.get('user')
}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`;
newMessage.file = { 'blob': b, 'name': 'rollDetails.txt' };
} }
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
gms.forEach(async e => { gms.forEach(async (e) => {
log(LT.LOG, `Messaging GM ${e} roll results`); log(LT.LOG, `Messaging GM ${e} roll results`);
// Attempt to DM the GMs and send a warning if it could not DM a GM // Attempt to DM the GMs and send a warning if it could not DM a GM
await sendDirectMessage(BigInt(e), newMessage).catch(async () => { await sendDirectMessage(BigInt(e), newMessage).catch(async () => {
const failedSend = generateDMFailed(e); const failedSend = generateDMFailed(e);
// Send the return message as a DM or normal message depening on if the channel is set // Send the return message as a DM or normal message depening on if the channel is set
if ((query.get("channel") || "").length > 0) { if ((query.get('channel') || '').length > 0) {
m = await sendMessage(BigInt(query.get("channel") || ""), failedSend).catch(() => { m = await sendMessage(BigInt(query.get('channel') || ''), failedSend).catch(() => {
requestEvent.respondWith(new Response("Message failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} else { } else {
m = await sendDirectMessage(BigInt(query.get("user") || ""), failedSend).catch(() => { m = await sendDirectMessage(BigInt(query.get('user') || ''), failedSend).catch(() => {
requestEvent.respondWith(new Response("Message failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} }
@ -359,7 +369,7 @@ const start = async (): Promise<void> => {
}); });
// Always log API rolls for abuse detection // Always log API rolls for abuse detection
dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, ((typeof m === "object") ? m.id : null)]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, (typeof m === 'object') ? m.id : null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
@ -378,33 +388,37 @@ const start = async (): Promise<void> => {
// When not a GM roll, make sure the message is not too big // When not a GM roll, make sure the message is not too big
if (returnText.length > 2000) { if (returnText.length > 2000) {
// If its too big, collapse it into a .txt file and send that instead. // If its too big, collapse it into a .txt file and send that instead.
const b = await new Blob([returnText as BlobPart], { "type": "text" }); const b = await new Blob([returnText as BlobPart], { 'type': 'text' });
if (b.size > 8388290) { if (b.size > 8388290) {
// Update return text // Update return text
newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; newMessage.content = `${apiPrefix}<@${
query.get('user')
}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`;
} else { } else {
// Update return text // Update return text
newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; newMessage.content = `${apiPrefix}<@${
newMessage.file = { "blob": b, "name": "rollDetails.txt" }; query.get('user')
}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`;
newMessage.file = { 'blob': b, 'name': 'rollDetails.txt' };
} }
} }
// Send the return message as a DM or normal message depening on if the channel is set // Send the return message as a DM or normal message depening on if the channel is set
if ((query.get("channel") || "").length > 0) { if ((query.get('channel') || '').length > 0) {
m = await sendMessage(BigInt(query.get("channel") || ""), newMessage).catch(() => { m = await sendMessage(BigInt(query.get('channel') || ''), newMessage).catch(() => {
requestEvent.respondWith(new Response("Message 20 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 20 failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} else { } else {
m = await sendDirectMessage(BigInt(query.get("user") || ""), newMessage).catch(() => { m = await sendDirectMessage(BigInt(query.get('user') || ''), newMessage).catch(() => {
requestEvent.respondWith(new Response("Message 21 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 21 failed to send.', { status: Status.InternalServerError }));
errorOut = true; errorOut = true;
}); });
} }
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, ((typeof m === "object") ? m.id : null)]).catch(e => { dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, (typeof m === 'object') ? m.id : null]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
}); });
@ -436,17 +450,17 @@ const start = async (): Promise<void> => {
break; break;
} }
break; break;
case "POST": case 'POST':
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case "/api/channel/add": case '/api/channel/add':
case "/api/channel/add/": case '/api/channel/add/':
if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0))) { if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0))) {
if (apiUserid === BigInt(query.get("user") || "0")) { if (apiUserid === BigInt(query.get('user') || '0')) {
// Flag to see if there is an error inside the catch // Flag to see if there is an error inside the catch
let erroredOut = false; let erroredOut = false;
// Insert new user/channel pair into the db // Insert new user/channel pair into the db
await dbClient.execute("INSERT INTO allowed_channels(userid,channelid) values(?,?)", [apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { await dbClient.execute('INSERT INTO allowed_channels(userid,channelid) values(?,?)', [apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -475,37 +489,37 @@ const start = async (): Promise<void> => {
break; break;
} }
break; break;
case "PUT": case 'PUT':
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case "/api/key/ban": case '/api/key/ban':
case "/api/key/ban/": case '/api/key/ban/':
case "/api/key/unban": case '/api/key/unban':
case "/api/key/unban/": case '/api/key/unban/':
case "/api/key/activate": case '/api/key/activate':
case "/api/key/activate/": case '/api/key/activate/':
case "/api/key/deactivate": case '/api/key/deactivate':
case "/api/key/deactivate/": case '/api/key/deactivate/':
if ((query.has("a") && ((query.get("a") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { if ((query.has('a') && ((query.get('a') || '').length > 0)) && (query.has('user') && ((query.get('user') || '').length > 0))) {
if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) {
// Flag to see if there is an error inside the catch // Flag to see if there is an error inside the catch
let key, value, erroredOut = false; let key, value, erroredOut = false;
// Determine key to edit // Determine key to edit
if (path.toLowerCase().indexOf("ban") > 0) { if (path.toLowerCase().indexOf('ban') > 0) {
key = "banned"; key = 'banned';
} else { } else {
key = "active"; key = 'active';
} }
// Determine value to set // Determine value to set
if (path.toLowerCase().indexOf("de") > 0 || path.toLowerCase().indexOf("un") > 0) { if (path.toLowerCase().indexOf('de') > 0 || path.toLowerCase().indexOf('un') > 0) {
value = 0; value = 0;
} else { } else {
value = 1; value = 1;
} }
// Execute the DB modification // Execute the DB modification
await dbClient.execute("UPDATE all_keys SET ?? = ? WHERE userid = ?", [key, value, apiUserid]).catch(e => { await dbClient.execute('UPDATE all_keys SET ?? = ? WHERE userid = ?', [key, value, apiUserid]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -528,24 +542,27 @@ const start = async (): Promise<void> => {
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
} }
break; break;
case "/api/channel/ban": case '/api/channel/ban':
case "/api/channel/ban/": case '/api/channel/ban/':
case "/api/channel/unban": case '/api/channel/unban':
case "/api/channel/unban/": case '/api/channel/unban/':
if ((query.has("a") && ((query.get("a") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { if (
if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { (query.has('a') && ((query.get('a') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0)) &&
(query.has('user') && ((query.get('user') || '').length > 0))
) {
if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) {
// Flag to see if there is an error inside the catch // Flag to see if there is an error inside the catch
let value, erroredOut = false; let value, erroredOut = false;
// Determine value to set // Determine value to set
if (path.toLowerCase().indexOf("un") > 0) { if (path.toLowerCase().indexOf('un') > 0) {
value = 0; value = 0;
} else { } else {
value = 1; value = 1;
} }
// Execute the DB modification // Execute the DB modification
await dbClient.execute("UPDATE allowed_channels SET banned = ? WHERE userid = ? AND channelid = ?", [value, apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { await dbClient.execute('UPDATE allowed_channels SET banned = ? WHERE userid = ? AND channelid = ?', [value, apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -568,24 +585,24 @@ const start = async (): Promise<void> => {
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
} }
break; break;
case "/api/channel/activate": case '/api/channel/activate':
case "/api/channel/activate/": case '/api/channel/activate/':
case "/api/channel/deactivate": case '/api/channel/deactivate':
case "/api/channel/deactivate/": case '/api/channel/deactivate/':
if ((query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { if ((query.has('channel') && ((query.get('channel') || '').length > 0)) && (query.has('user') && ((query.get('user') || '').length > 0))) {
if (apiUserid === BigInt(query.get("user") || "0")) { if (apiUserid === BigInt(query.get('user') || '0')) {
// Flag to see if there is an error inside the catch // Flag to see if there is an error inside the catch
let value, erroredOut = false; let value, erroredOut = false;
// Determine value to set // Determine value to set
if (path.toLowerCase().indexOf("de") > 0) { if (path.toLowerCase().indexOf('de') > 0) {
value = 0; value = 0;
} else { } else {
value = 1; value = 1;
} }
// Update the requested entry // Update the requested entry
await dbClient.execute("UPDATE allowed_channels SET active = ? WHERE userid = ? AND channelid = ?", [value, apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { await dbClient.execute('UPDATE allowed_channels SET active = ? WHERE userid = ? AND channelid = ?', [value, apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -601,7 +618,7 @@ const start = async (): Promise<void> => {
} }
} else { } else {
// Alert API user that they shouldn't be doing this // Alert API user that they shouldn't be doing this
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.Forbidden), { status: Status.Forbidden })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.Forbidden), { status: Status.Forbidden }));
} }
} else { } else {
// Alert API user that they messed up // Alert API user that they messed up
@ -614,18 +631,18 @@ const start = async (): Promise<void> => {
break; break;
} }
break; break;
case "DELETE": case 'DELETE':
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case "/api/key/delete": case '/api/key/delete':
case "/api/key/delete/": case '/api/key/delete/':
if (query.has("user") && ((query.get("user") || "").length > 0) && query.has("email") && ((query.get("email") || "").length > 0)) { if (query.has('user') && ((query.get('user') || '').length > 0) && query.has('email') && ((query.get('email') || '').length > 0)) {
if (apiUserid === BigInt(query.get("user") || "0") && apiUserEmail === query.get("email")) { if (apiUserid === BigInt(query.get('user') || '0') && apiUserEmail === query.get('email')) {
if (query.has("code") && ((query.get("code") || "").length > 0)) { if (query.has('code') && ((query.get('code') || '').length > 0)) {
if ((query.get("code") || "") === apiUserDelCode) { if ((query.get('code') || '') === apiUserDelCode) {
// User has recieved their delete code and we need to delete the account now // User has recieved their delete code and we need to delete the account now
let erroredOut = false; let erroredOut = false;
await dbClient.execute("DELETE FROM allowed_channels WHERE userid = ?", [apiUserid]).catch(e => { await dbClient.execute('DELETE FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -634,7 +651,7 @@ const start = async (): Promise<void> => {
break; break;
} }
await dbClient.execute("DELETE FROM all_keys WHERE userid = ?", [apiUserid]).catch(e => { await dbClient.execute('DELETE FROM all_keys WHERE userid = ?', [apiUserid]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -657,7 +674,7 @@ const start = async (): Promise<void> => {
let erroredOut = false; let erroredOut = false;
// Execute the DB modification // Execute the DB modification
await dbClient.execute("UPDATE all_keys SET deleteCode = ? WHERE userid = ?", [deleteCode, apiUserid]).catch(e => { await dbClient.execute('UPDATE all_keys SET deleteCode = ? WHERE userid = ?', [deleteCode, apiUserid]).catch((e) => {
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
@ -668,7 +685,7 @@ const start = async (): Promise<void> => {
// "Send" the email // "Send" the email
await sendMessage(config.api.email, generateApiDeleteEmail(apiUserEmail, deleteCode)).catch(() => { await sendMessage(config.api.email, generateApiDeleteEmail(apiUserEmail, deleteCode)).catch(() => {
requestEvent.respondWith(new Response("Message 30 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 30 failed to send.', { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
}); });
if (erroredOut) { if (erroredOut) {
@ -685,7 +702,7 @@ const start = async (): Promise<void> => {
} }
} else { } else {
// Alert API user that they messed up // Alert API user that they messed up
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest }));
} }
break; break;
default: default:
@ -706,25 +723,25 @@ const start = async (): Promise<void> => {
} }
} else if (!authenticated && !rateLimited) { } else if (!authenticated && !rateLimited) {
// Get path and query as a string // Get path and query as a string
const [path, tempQ] = request.url.split("?"); const [path, tempQ] = request.url.split('?');
// Turn the query into a map (if it exists) // Turn the query into a map (if it exists)
const query = new Map<string, string>(); const query = new Map<string, string>();
if (tempQ !== undefined) { if (tempQ !== undefined) {
tempQ.split("&").forEach((e: string) => { tempQ.split('&').forEach((e: string) => {
log(LT.LOG, `Parsing request query #2 ${request} ${e}`); log(LT.LOG, `Parsing request query #2 ${request} ${e}`);
const [option, params] = e.split("="); const [option, params] = e.split('=');
query.set(option.toLowerCase(), params); query.set(option.toLowerCase(), params);
}); });
} }
// Handle the request // Handle the request
switch (request.method) { switch (request.method) {
case "GET": case 'GET':
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case "/api/key": case '/api/key':
case "/api/key/": case '/api/key/':
if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("email") && ((query.get("email") || "").length > 0))) { if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('email') && ((query.get('email') || '').length > 0))) {
// Generate new secure key // Generate new secure key
const newKey = await nanoid(25); const newKey = await nanoid(25);
@ -732,20 +749,22 @@ const start = async (): Promise<void> => {
let erroredOut = false; let erroredOut = false;
// Insert new key/user pair into the db // Insert new key/user pair into the db
await dbClient.execute("INSERT INTO all_keys(userid,apiKey,email) values(?,?,?)", [BigInt(query.get("user") || "0"), newKey, (query.get("email") || "").toLowerCase()]).catch(e => { await dbClient.execute('INSERT INTO all_keys(userid,apiKey,email) values(?,?,?)', [BigInt(query.get('user') || '0'), newKey, (query.get('email') || '').toLowerCase()]).catch(
log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); (e) => {
requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`);
erroredOut = true; requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError }));
}); erroredOut = true;
},
);
// Exit this case now if catch errored // Exit this case now if catch errored
if (erroredOut) { if (erroredOut) {
break; break;
} }
// "Send" the email // "Send" the email
await sendMessage(config.api.email, generateApiKeyEmail(query.get("email") || "no email", newKey)).catch(() => { await sendMessage(config.api.email, generateApiKeyEmail(query.get('email') || 'no email', newKey)).catch(() => {
requestEvent.respondWith(new Response("Message 31 failed to send.", { status: Status.InternalServerError })); requestEvent.respondWith(new Response('Message 31 failed to send.', { status: Status.InternalServerError }));
erroredOut = true; erroredOut = true;
}); });

View File

@ -1,16 +1,16 @@
import { ping } from "./ping.ts"; import { ping } from './ping.ts';
import { rip } from "./rip.ts"; import { rip } from './rip.ts';
import { rollHelp } from "./rollHelp.ts"; import { rollHelp } from './rollHelp.ts';
import { help } from "./help.ts"; import { help } from './help.ts';
import { info } from "./info.ts"; import { info } from './info.ts';
import { privacy } from "./privacy.ts"; import { privacy } from './privacy.ts';
import { version } from "./version.ts"; import { version } from './version.ts';
import { report } from "./report.ts"; import { report } from './report.ts';
import { stats } from "./stats.ts"; import { stats } from './stats.ts';
import { api } from "./apiCmd.ts"; import { api } from './apiCmd.ts';
import { emoji } from "./emoji.ts"; import { emoji } from './emoji.ts';
import { roll } from "./roll.ts"; import { roll } from './roll.ts';
import { handleMentions } from "./handleMentions.ts"; import { handleMentions } from './handleMentions.ts';
export default { export default {
ping, ping,
@ -25,5 +25,5 @@ export default {
api, api,
emoji, emoji,
roll, roll,
handleMentions handleMentions,
}; };

View File

@ -1,65 +1,57 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, hasGuildPermissions, DiscordenoMessage,
hasGuildPermissions,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import apiCommands from "./apiCmd/_index.ts"; import apiCommands from './apiCmd/_index.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const api = async (message: DiscordenoMessage, args: string[]) => { export const api = async (message: DiscordenoMessage, args: string[]) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("api");`).catch(e => { dbClient.execute(`CALL INC_CNT("api");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
// Local apiArg in lowercase // Local apiArg in lowercase
const apiArg = (args[0] || "help").toLowerCase(); const apiArg = (args[0] || 'help').toLowerCase();
// Alert users who DM the bot that this command is for guilds only // Alert users who DM the bot that this command is for guilds only
if (message.guildId === 0n) { if (message.guildId === 0n) {
message.send(constantCmds.apiGuildOnly).catch(e => { message.send(constantCmds.apiGuildOnly).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
return; return;
} }
// Makes sure the user is authenticated to run the API command // Makes sure the user is authenticated to run the API command
if (await hasGuildPermissions(message.authorId, message.guildId, ["ADMINISTRATOR"])) { if (await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR'])) {
// [[api help // [[api help
// Shows API help details // Shows API help details
if (apiArg === "help") { if (apiArg === 'help') {
apiCommands.help(message); apiCommands.help(message);
} } // [[api allow/block
// [[api allow/block
// Lets a guild admin allow or ban API rolls from happening in said guild // Lets a guild admin allow or ban API rolls from happening in said guild
else if (apiArg === "allow" || apiArg === "block" || apiArg === "enable" || apiArg === "disable") { else if (apiArg === 'allow' || apiArg === 'block' || apiArg === 'enable' || apiArg === 'disable') {
apiCommands.allowBlock(message, apiArg); apiCommands.allowBlock(message, apiArg);
} } // [[api delete
// [[api delete
// Lets a guild admin delete their server from the database // Lets a guild admin delete their server from the database
else if (apiArg === "delete") { else if (apiArg === 'delete') {
apiCommands.deleteGuild(message); apiCommands.deleteGuild(message);
} } // [[api status
// [[api status
// Lets a guild admin check the status of API rolling in said guild // Lets a guild admin check the status of API rolling in said guild
else if (apiArg === "status") { else if (apiArg === 'status') {
apiCommands.status(message); apiCommands.status(message);
} } // [[api show-warn/hide-warn
// [[api show-warn/hide-warn
// Lets a guild admin decide if the API warning should be shown on messages from the API // Lets a guild admin decide if the API warning should be shown on messages from the API
else if (apiArg === "show-warn" || apiArg === "hide-warn") { else if (apiArg === 'show-warn' || apiArg === 'hide-warn') {
apiCommands.showHideWarn(message, apiArg); apiCommands.showHideWarn(message, apiArg);
} }
} else { } else {
message.send(constantCmds.apiPermError).catch(e => { message.send(constantCmds.apiPermError).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,13 +1,13 @@
import { help } from "./apiHelp.ts"; import { help } from './apiHelp.ts';
import { allowBlock } from "./allowBlock.ts"; import { allowBlock } from './allowBlock.ts';
import { deleteGuild } from "./deleteGuild.ts"; import { deleteGuild } from './deleteGuild.ts';
import { status } from "./status.ts"; import { status } from './status.ts';
import { showHideWarn } from "./showHideWarn.ts"; import { showHideWarn } from './showHideWarn.ts';
export default { export default {
help, help,
allowBlock, allowBlock,
deleteGuild, deleteGuild,
status, status,
showHideWarn showHideWarn,
}; };

View File

@ -1,17 +1,17 @@
import { dbClient } from "../../db.ts"; import { dbClient } from '../../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { generateApiFailed, generateApiSuccess } from "../../constantCmds.ts"; import { generateApiFailed, generateApiSuccess } from '../../constantCmds.ts';
export const allowBlock = async (message: DiscordenoMessage, apiArg: string) => { export const allowBlock = async (message: DiscordenoMessage, apiArg: string) => {
const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`);
message.send(generateApiFailed(apiArg)).catch(e1 => { message.send(generateApiFailed(apiArg)).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
@ -19,26 +19,30 @@ export const allowBlock = async (message: DiscordenoMessage, apiArg: string) =>
if (guildQuery.length === 0) { if (guildQuery.length === 0) {
// Since guild is not in our DB, add it in // Since guild is not in our DB, add it in
await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,active) values(?,?,?)`, [message.guildId, message.channelId, ((apiArg === "allow" || apiArg === "enable") ? 1 : 0)]).catch(e0 => { await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,active) values(?,?,?)`, [message.guildId, message.channelId, (apiArg === 'allow' || apiArg === 'enable') ? 1 : 0]).catch(
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`); (e0) => {
message.send(generateApiFailed(apiArg)).catch(e1 => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`);
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); message.send(generateApiFailed(apiArg)).catch((e1) => {
}); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
return; });
}); return;
},
);
} else { } else {
// Since guild is in our DB, update it // Since guild is in our DB, update it
await dbClient.execute(`UPDATE allowed_guilds SET active = ? WHERE guildid = ? AND channelid = ?`, [((apiArg === "allow" || apiArg === "enable") ? 1 : 0), message.guildId, message.channelId]).catch(e0 => { await dbClient.execute(`UPDATE allowed_guilds SET active = ? WHERE guildid = ? AND channelid = ?`, [(apiArg === 'allow' || apiArg === 'enable') ? 1 : 0, message.guildId, message.channelId]).catch(
log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`); (e0) => {
message.send(generateApiFailed(apiArg)).catch(e1 => { log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`);
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); message.send(generateApiFailed(apiArg)).catch((e1) => {
}); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
return; });
}); return;
},
);
} }
// We won't get here if there's any errors, so we know it has bee successful, so report as such // We won't get here if there's any errors, so we know it has bee successful, so report as such
message.send(generateApiSuccess(`${apiArg}ed`)).catch(e => { message.send(generateApiSuccess(`${apiArg}ed`)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,14 +1,14 @@
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { constantCmds } from "../../constantCmds.ts"; import { constantCmds } from '../../constantCmds.ts';
export const help = (message: DiscordenoMessage) => { export const help = (message: DiscordenoMessage) => {
message.send(constantCmds.apiHelp).catch(e => { message.send(constantCmds.apiHelp).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,24 +1,24 @@
import { dbClient } from "../../db.ts"; import { dbClient } from '../../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { constantCmds } from "../../constantCmds.ts"; import { constantCmds } from '../../constantCmds.ts';
export const deleteGuild = async (message: DiscordenoMessage) => { export const deleteGuild = async (message: DiscordenoMessage) => {
await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`);
message.send(constantCmds.apiDeleteFail).catch(e1 => { message.send(constantCmds.apiDeleteFail).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
}); });
// We won't get here if there's any errors, so we know it has bee successful, so report as such // We won't get here if there's any errors, so we know it has bee successful, so report as such
message.send(constantCmds.apiRemoveGuild).catch(e => { message.send(constantCmds.apiRemoveGuild).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,17 +1,17 @@
import { dbClient } from "../../db.ts"; import { dbClient } from '../../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { generateApiFailed, generateApiSuccess } from "../../constantCmds.ts"; import { generateApiFailed, generateApiSuccess } from '../../constantCmds.ts';
export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) => { export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) => {
const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`);
message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
@ -19,18 +19,18 @@ export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) =
if (guildQuery.length === 0) { if (guildQuery.length === 0) {
// Since guild is not in our DB, add it in // Since guild is not in our DB, add it in
await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,hidewarn) values(?,?,?)`, [message.guildId, message.channelId, ((apiArg === "hide-warn") ? 1 : 0)]).catch(e0 => { await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,hidewarn) values(?,?,?)`, [message.guildId, message.channelId, (apiArg === 'hide-warn') ? 1 : 0]).catch((e0) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`);
message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
}); });
} else { } else {
// Since guild is in our DB, update it // Since guild is in our DB, update it
await dbClient.execute(`UPDATE allowed_guilds SET hidewarn = ? WHERE guildid = ? AND channelid = ?`, [((apiArg === "hide-warn") ? 1 : 0), message.guildId, message.channelId]).catch(e0 => { await dbClient.execute(`UPDATE allowed_guilds SET hidewarn = ? WHERE guildid = ? AND channelid = ?`, [(apiArg === 'hide-warn') ? 1 : 0, message.guildId, message.channelId]).catch((e0) => {
log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`);
message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
@ -38,7 +38,7 @@ export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) =
} }
// We won't get here if there's any errors, so we know it has bee successful, so report as such // We won't get here if there's any errors, so we know it has bee successful, so report as such
message.send(generateApiSuccess(apiArg)).catch(e => { message.send(generateApiSuccess(apiArg)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,18 +1,18 @@
import { dbClient } from "../../db.ts"; import { dbClient } from '../../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { constantCmds, generateApiStatus } from "../../constantCmds.ts"; import { constantCmds, generateApiStatus } from '../../constantCmds.ts';
export const status = async (message: DiscordenoMessage) => { export const status = async (message: DiscordenoMessage) => {
// Get status of guild from the db // Get status of guild from the db
const guildQuery = await dbClient.query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { const guildQuery = await dbClient.query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`);
message.send(constantCmds.apiStatusFail).catch(e1 => { message.send(constantCmds.apiStatusFail).catch((e1) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`);
}); });
return; return;
@ -22,17 +22,17 @@ export const status = async (message: DiscordenoMessage) => {
if (guildQuery.length > 0) { if (guildQuery.length > 0) {
// Check if guild is banned from using API and return appropriate message // Check if guild is banned from using API and return appropriate message
if (guildQuery[0].banned) { if (guildQuery[0].banned) {
message.send(generateApiStatus(true, false)).catch(e => { message.send(generateApiStatus(true, false)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else { } else {
message.send(generateApiStatus(false, guildQuery[0].active)).catch(e => { message.send(generateApiStatus(false, guildQuery[0].active)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} }
} else { } else {
// Guild is not in DB, therefore they are blocked // Guild is not in DB, therefore they are blocked
message.send(generateApiStatus(false, false)).catch(e => { message.send(generateApiStatus(false, false)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,40 +1,40 @@
import config from "../../config.ts"; import config from '../../config.ts';
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { EmojiConf } from "../mod.d.ts"; import { EmojiConf } from '../mod.d.ts';
const allEmojiAliases: string[] = []; const allEmojiAliases: string[] = [];
config.emojis.forEach((emoji: EmojiConf) => { config.emojis.forEach((emoji: EmojiConf) => {
allEmojiAliases.push(...emoji.aliases) allEmojiAliases.push(...emoji.aliases);
}); });
export const emoji = (message: DiscordenoMessage, command: string) => { export const emoji = (message: DiscordenoMessage, command: string) => {
// shortcut // shortcut
if (allEmojiAliases.indexOf(command)) { if (allEmojiAliases.indexOf(command)) {
// Start looping thru the possible emojis // Start looping thru the possible emojis
config.emojis.some((emoji: EmojiConf) => { config.emojis.some((emoji: EmojiConf) => {
log(LT.LOG, `Checking if command was emoji ${JSON.stringify(emoji)}`); log(LT.LOG, `Checking if command was emoji ${JSON.stringify(emoji)}`);
// If a match gets found // If a match gets found
if (emoji.aliases.indexOf(command || "") > -1) { if (emoji.aliases.indexOf(command || '') > -1) {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("emojis");`).catch(e => { dbClient.execute(`CALL INC_CNT("emojis");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
// Send the needed emoji1 // Send the needed emoji1
message.send(`<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}>`).catch(e => { message.send(`<${emoji.animated ? 'a' : ''}:${emoji.name}:${emoji.id}>`).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
// And attempt to delete if needed // And attempt to delete if needed
if (emoji.deleteSender) { if (emoji.deleteSender) {
message.delete().catch(e => { message.delete().catch((e) => {
log(LT.WARN, `Failed to delete message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.WARN, `Failed to delete message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,22 +1,22 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const handleMentions = (message: DiscordenoMessage) => { export const handleMentions = (message: DiscordenoMessage) => {
log(LT.LOG, `Handling @mention message: ${JSON.stringify(message)}`); log(LT.LOG, `Handling @mention message: ${JSON.stringify(message)}`);
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("mention");`).catch(e => { dbClient.execute(`CALL INC_CNT("mention");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.mention).catch(e => { message.send(constantCmds.mention).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const help = (message: DiscordenoMessage) => { export const help = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("help");`).catch(e => { dbClient.execute(`CALL INC_CNT("help");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.help).catch(e => { message.send(constantCmds.help).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const info = (message: DiscordenoMessage) => { export const info = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("info");`).catch(e => { dbClient.execute(`CALL INC_CNT("info");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.info).catch(e => { message.send(constantCmds.info).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,16 +1,16 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { generatePing } from "../constantCmds.ts"; import { generatePing } from '../constantCmds.ts';
export const ping = async (message: DiscordenoMessage) => { export const ping = async (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("ping");`).catch(e => { dbClient.execute(`CALL INC_CNT("ping");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const privacy = (message: DiscordenoMessage) => { export const privacy = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("privacy");`).catch(e => { dbClient.execute(`CALL INC_CNT("privacy");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.privacy).catch(e => { message.send(constantCmds.privacy).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,29 +1,30 @@
import config from "../../config.ts"; import config from '../../config.ts';
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, sendMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; sendMessage,
import { constantCmds, generateReport } from "../constantCmds.ts"; } from '../../deps.ts';
import { constantCmds, generateReport } from '../constantCmds.ts';
export const report = (message: DiscordenoMessage, args: string[]) => { export const report = (message: DiscordenoMessage, args: string[]) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("report");`).catch(e => { dbClient.execute(`CALL INC_CNT("report");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
if (args.join(" ")) { if (args.join(' ')) {
sendMessage(config.reportChannel, generateReport(args.join(" "))).catch(e => { sendMessage(config.reportChannel, generateReport(args.join(' '))).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.report).catch(e => { message.send(constantCmds.report).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} else { } else {
message.send(constantCmds.reportFail).catch(e => { message.send(constantCmds.reportFail).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const rip = (message: DiscordenoMessage) => { export const rip = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("rip");`).catch(e => { dbClient.execute(`CALL INC_CNT("rip");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.rip).catch(e => { message.send(constantCmds.rip).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,26 +1,27 @@
import config from "../../config.ts"; import config from '../../config.ts';
import { DEVMODE } from "../../flags.ts"; import { DEVMODE } from '../../flags.ts';
import { dbClient, queries } from "../db.ts"; import { dbClient, queries } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, sendDirectMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; sendDirectMessage,
import solver from "../solver/_index.ts"; } from '../../deps.ts';
import { constantCmds, generateDMFailed } from "../constantCmds.ts"; import solver from '../solver/_index.ts';
import rollFuncs from "./roll/_index.ts"; import { constantCmds, generateDMFailed } from '../constantCmds.ts';
import rollFuncs from './roll/_index.ts';
export const roll = async (message: DiscordenoMessage, args: string[], command: string) => { export const roll = async (message: DiscordenoMessage, args: string[], command: string) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("roll");`).catch(e => { dbClient.execute(`CALL INC_CNT("roll");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
// If DEVMODE is on, only allow this command to be used in the devServer // If DEVMODE is on, only allow this command to be used in the devServer
if (DEVMODE && message.guildId !== config.devServer) { if (DEVMODE && message.guildId !== config.devServer) {
message.send(constantCmds.indev).catch(e => { message.send(constantCmds.indev).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
return; return;
@ -28,7 +29,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
// Rest of this command is in a try-catch to protect all sends/edits from erroring out // Rest of this command is in a try-catch to protect all sends/edits from erroring out
try { try {
const originalCommand = `${config.prefix}${command} ${args.join(" ")}`; const originalCommand = `${config.prefix}${command} ${args.join(' ')}`;
const m = await message.send(constantCmds.rolling); const m = await message.send(constantCmds.rolling);
@ -41,10 +42,10 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
} }
// Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in // Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in
const rollCmd = `${command} ${args.join(" ")}`; const rollCmd = `${command} ${args.join(' ')}`;
const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: "EmptyMessage", errorMsg: "Error: Empty message", line1: "", line2: "", line3: "" }; const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message', line1: '', line2: '', line3: '' };
let returnText = ""; let returnText = '';
// If there was an error, report it to the user in hopes that they can determine what they did wrong // If there was an error, report it to the user in hopes that they can determine what they did wrong
if (returnmsg.error) { if (returnmsg.error) {
@ -53,7 +54,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }
@ -64,7 +65,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
if (!modifiers.superNoDetails) { if (!modifiers.superNoDetails) {
if (modifiers.noDetails) { if (modifiers.noDetails) {
returnText += "\nDetails suppressed by -nd flag."; returnText += '\nDetails suppressed by -nd flag.';
} else { } else {
returnText += `\nDetails:\n${modifiers.spoiler}${returnmsg.line3}${modifiers.spoiler}`; returnText += `\nDetails:\n${modifiers.spoiler}${returnmsg.line3}${modifiers.spoiler}`;
} }
@ -74,30 +75,31 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
// If the roll was a GM roll, send DMs to all the GMs // If the roll was a GM roll, send DMs to all the GMs
if (modifiers.gmRoll) { if (modifiers.gmRoll) {
// Make a new return line to be sent to the roller // Make a new return line to be sent to the roller
const normalText = `<@${message.authorId}>${returnmsg.line1}\nResults have been messaged to the following GMs: ${modifiers.gms.join(" ")}`; const normalText = `<@${message.authorId}>${returnmsg.line1}\nResults have been messaged to the following GMs: ${modifiers.gms.join(' ')}`;
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
modifiers.gms.forEach(async e => { modifiers.gms.forEach(async (e) => {
log(LT.LOG, `Messaging GM ${e}`); log(LT.LOG, `Messaging GM ${e}`);
// If its too big, collapse it into a .txt file and send that instead. // If its too big, collapse it into a .txt file and send that instead.
const b = await new Blob([returnText as BlobPart], { "type": "text" }); const b = await new Blob([returnText as BlobPart], { 'type': 'text' });
if (b.size > 8388290) { if (b.size > 8388290) {
// Update return text // Update return text
// todo: embedify // todo: embedify
returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; returnText =
`<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`;
// Attempt to DM the GMs and send a warning if it could not DM a GM // Attempt to DM the GMs and send a warning if it could not DM a GM
await sendDirectMessage(BigInt(e.substring(2, (e.length - 1))), returnText).catch(() => { await sendDirectMessage(BigInt(e.substring(2, e.length - 1)), returnText).catch(() => {
message.send(generateDMFailed(e)); message.send(generateDMFailed(e));
}); });
} else { } else {
// Update return // Update return
// todo: embedify // todo: embedify
returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`; returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`;
// Attempt to DM the GMs and send a warning if it could not DM a GM // Attempt to DM the GMs and send a warning if it could not DM a GM
await sendDirectMessage(BigInt(e.substring(2, (e.length - 1))), { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }).catch(() => { await sendDirectMessage(BigInt(e.substring(2, e.length - 1)), { 'content': returnText, 'file': { 'blob': b, 'name': 'rollDetails.txt' } }).catch(() => {
message.send(generateDMFailed(e)); message.send(generateDMFailed(e));
}); });
} }
@ -108,7 +110,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }
@ -116,23 +118,25 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
// When not a GM roll, make sure the message is not too big // When not a GM roll, make sure the message is not too big
if (returnText.length > 2000) { if (returnText.length > 2000) {
// If its too big, collapse it into a .txt file and send that instead. // If its too big, collapse it into a .txt file and send that instead.
const b = await new Blob([returnText as BlobPart], { "type": "text" }); const b = await new Blob([returnText as BlobPart], { 'type': 'text' });
if (b.size > 8388290) { if (b.size > 8388290) {
// Update return text // Update return text
returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; returnText =
`<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`;
// Send the results // Send the results
m.edit(returnText); m.edit(returnText);
} else { } else {
// Update return text // Update return text
returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; returnText =
`<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`;
// Remove the original message to send new one with attachment // Remove the original message to send new one with attachment
m.delete(); m.delete();
// todo: embedify // todo: embedify
await message.send({ "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }); await message.send({ 'content': returnText, 'file': { 'blob': b, 'name': 'rollDetails.txt' } });
} }
} else { } else {
// Finally send the text // Finally send the text
@ -141,7 +145,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,5 +1,5 @@
import { getModifiers } from "./getModifiers.ts"; import { getModifiers } from './getModifiers.ts';
export default { export default {
getModifiers getModifiers,
}; };

View File

@ -1,88 +1,88 @@
import config from "../../../config.ts"; import config from '../../../config.ts';
import { DEVMODE } from "../../../flags.ts"; import { DEVMODE } from '../../../flags.ts';
import { dbClient, queries } from "../../db.ts"; import { dbClient, queries } from '../../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../../deps.ts"; } from '../../../deps.ts';
import { generateRollError } from "../../constantCmds.ts"; import { generateRollError } from '../../constantCmds.ts';
import { RollModifiers } from "../../mod.d.ts"; import { RollModifiers } from '../../mod.d.ts';
export const getModifiers = (m: DiscordenoMessage, args: string[], command: string, originalCommand: string): RollModifiers => { export const getModifiers = (m: DiscordenoMessage, args: string[], command: string, originalCommand: string): RollModifiers => {
const errorType = "Modifiers invalid:"; const errorType = 'Modifiers invalid:';
const modifiers: RollModifiers = { const modifiers: RollModifiers = {
noDetails: false, noDetails: false,
superNoDetails: false, superNoDetails: false,
spoiler: "", spoiler: '',
maxRoll: false, maxRoll: false,
nominalRoll: false, nominalRoll: false,
gmRoll: false, gmRoll: false,
gms: [], gms: [],
order: "", order: '',
valid: false, valid: false,
count: false count: false,
}; };
// Check if any of the args are command flags and pull those out into the modifiers object // Check if any of the args are command flags and pull those out into the modifiers object
for (let i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
log(LT.LOG, `Checking ${command}${args.join(" ")} for command modifiers ${i}`); log(LT.LOG, `Checking ${command}${args.join(' ')} for command modifiers ${i}`);
let defaultCase = false; let defaultCase = false;
switch (args[i].toLowerCase()) { switch (args[i].toLowerCase()) {
case "-c": case '-c':
modifiers.count = true; modifiers.count = true;
break; break;
case "-nd": case '-nd':
modifiers.noDetails = true; modifiers.noDetails = true;
break; break;
case "-snd": case '-snd':
modifiers.superNoDetails = true; modifiers.superNoDetails = true;
break; break;
case "-s": case '-s':
modifiers.spoiler = "||"; modifiers.spoiler = '||';
break; break;
case "-m": case '-m':
modifiers.maxRoll = true; modifiers.maxRoll = true;
break; break;
case "-n": case '-n':
modifiers.nominalRoll = true; modifiers.nominalRoll = true;
break; break;
case "-gm": case '-gm':
modifiers.gmRoll = true; modifiers.gmRoll = true;
// -gm is a little more complex, as we must get all of the GMs that need to be DMd // -gm is a little more complex, as we must get all of the GMs that need to be DMd
while (((i + 1) < args.length) && args[i + 1].startsWith("<@")) { while (((i + 1) < args.length) && args[i + 1].startsWith('<@')) {
log(LT.LOG, `Finding all GMs, checking args ${JSON.stringify(args)}`); log(LT.LOG, `Finding all GMs, checking args ${JSON.stringify(args)}`);
// Keep looping thru the rest of the args until one does not start with the discord mention code // Keep looping thru the rest of the args until one does not start with the discord mention code
modifiers.gms.push(args[i + 1].replace(/[!]/g, "")); modifiers.gms.push(args[i + 1].replace(/[!]/g, ''));
args.splice((i + 1), 1); args.splice(i + 1, 1);
} }
if (modifiers.gms.length < 1) { if (modifiers.gms.length < 1) {
// If -gm is on and none were found, throw an error // If -gm is on and none were found, throw an error
m.edit(generateRollError(errorType, "Must specifiy at least one GM by @mentioning them")); m.edit(generateRollError(errorType, 'Must specifiy at least one GM by @mentioning them'));
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "NoGMsFound", m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'NoGMsFound', m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }
return modifiers; return modifiers;
} }
break; break;
case "-o": case '-o':
// Shift the -o out of the array so the next item is the direction // Shift the -o out of the array so the next item is the direction
args.splice(i, 1); args.splice(i, 1);
if (!args[i] || args[i].toLowerCase()[0] !== "d" && args[i].toLowerCase()[0] !== "a") { if (!args[i] || args[i].toLowerCase()[0] !== 'd' && args[i].toLowerCase()[0] !== 'a') {
// If -o is on and asc or desc was not specified, error out // If -o is on and asc or desc was not specified, error out
m.edit(generateRollError(errorType, "Must specifiy `a` or `d` to order the rolls ascending or descending")); m.edit(generateRollError(errorType, 'Must specifiy `a` or `d` to order the rolls ascending or descending'));
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "NoOrderFound", m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'NoOrderFound', m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }
@ -105,11 +105,11 @@ export const getModifiers = (m: DiscordenoMessage, args: string[], command: stri
// maxRoll and nominalRoll cannot both be on, throw an error // maxRoll and nominalRoll cannot both be on, throw an error
if (modifiers.maxRoll && modifiers.nominalRoll) { if (modifiers.maxRoll && modifiers.nominalRoll) {
m.edit(generateRollError(errorType, "Cannot maximise and nominise the roll at the same time")); m.edit(generateRollError(errorType, 'Cannot maximise and nominise the roll at the same time'));
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "MaxAndNominal", m.id]).catch(e => { dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'MaxAndNominal', m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
}); });
} }

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const rollHelp = (message: DiscordenoMessage) => { export const rollHelp = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("rollhelp");`).catch(e => { dbClient.execute(`CALL INC_CNT("rollhelp");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.rollHelp).catch(e => { message.send(constantCmds.rollHelp).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,16 +1,18 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
cache, cacheHandlers, DiscordenoMessage, cache,
cacheHandlers,
DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds, generateStats } from "../constantCmds.ts"; import { constantCmds, generateStats } from '../constantCmds.ts';
export const stats = async (message: DiscordenoMessage) => { export const stats = async (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("stats");`).catch(e => { dbClient.execute(`CALL INC_CNT("stats");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
@ -18,19 +20,19 @@ export const stats = async (message: DiscordenoMessage) => {
const m = await message.send(constantCmds.loadingStats); const m = await message.send(constantCmds.loadingStats);
// Calculate how many times commands have been run // Calculate how many times commands have been run
const rollQuery = await dbClient.query(`SELECT count FROM command_cnt WHERE command = "roll";`).catch(e => { const rollQuery = await dbClient.query(`SELECT count FROM command_cnt WHERE command = "roll";`).catch((e) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`);
}); });
const totalQuery = await dbClient.query(`SELECT SUM(count) as count FROM command_cnt;`).catch(e => { const totalQuery = await dbClient.query(`SELECT SUM(count) as count FROM command_cnt;`).catch((e) => {
log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`);
}); });
const rolls = BigInt(rollQuery[0].count); const rolls = BigInt(rollQuery[0].count);
const total = BigInt(totalQuery[0].count); const total = BigInt(totalQuery[0].count);
const cachedGuilds = await cacheHandlers.size("guilds"); const cachedGuilds = await cacheHandlers.size('guilds');
const cachedChannels = await cacheHandlers.size("channels"); const cachedChannels = await cacheHandlers.size('channels');
const cachedMembers = await cacheHandlers.size("members"); const cachedMembers = await cacheHandlers.size('members');
m.edit(generateStats(cachedGuilds + cache.dispatchedGuildIds.size, cachedChannels + cache.dispatchedChannelIds.size, cachedMembers, rolls, total - rolls)).catch(e => { m.edit(generateStats(cachedGuilds + cache.dispatchedGuildIds.size, cachedChannels + cache.dispatchedChannelIds.size, cachedMembers, rolls, total - rolls)).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
} catch (e) { } catch (e) {

View File

@ -1,20 +1,20 @@
import { dbClient } from "../db.ts"; import { dbClient } from '../db.ts';
import { import {
// Discordeno deps // Discordeno deps
DiscordenoMessage, DiscordenoMessage,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { constantCmds } from "../constantCmds.ts"; import { constantCmds } from '../constantCmds.ts';
export const version = (message: DiscordenoMessage) => { export const version = (message: DiscordenoMessage) => {
// Light telemetry to see how many times a command is being run // Light telemetry to see how many times a command is being run
dbClient.execute(`CALL INC_CNT("version");`).catch(e => { dbClient.execute(`CALL INC_CNT("version");`).catch((e) => {
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
}); });
message.send(constantCmds.version).catch(e => { message.send(constantCmds.version).catch((e) => {
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
}); });
}; };

View File

@ -1,5 +1,5 @@
import config from "../config.ts"; import config from '../config.ts';
import { CountDetails } from "./solver/solver.d.ts"; import { CountDetails } from './solver/solver.d.ts';
const failColor = 0xe71212; const failColor = 0xe71212;
const warnColor = 0xe38f28; const warnColor = 0xe38f28;
@ -11,211 +11,231 @@ export const constantCmds = {
apiDeleteFail: { apiDeleteFail: {
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "Failed to delete this guild from the database.", title: 'Failed to delete this guild from the database.',
description: "If this issue persists, please report this to the developers." description: 'If this issue persists, please report this to the developers.',
}] }],
}, },
apiGuildOnly: { apiGuildOnly: {
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "API commands are only available in guilds." title: 'API commands are only available in guilds.',
}] }],
}, },
apiHelp: { apiHelp: {
embeds: [ embeds: [
{ {
color: infoColor2, color: infoColor2,
title: "The Artificer's API Details:", title: 'The Artificer\'s API Details:',
description: `The Artificer has a built in API that allows user to roll dice into Discord using third party programs. By default, API rolls are blocked from being sent in your guild. The API warning is also enabled by default. These commands may only be used by the Owner or Admins of your guild. description:
`The Artificer has a built in API that allows user to roll dice into Discord using third party programs. By default, API rolls are blocked from being sent in your guild. The API warning is also enabled by default. These commands may only be used by the Owner or Admins of your guild.
For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer). For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer).
You may enable and disable the API rolls for your guild as needed.` You may enable and disable the API rolls for your guild as needed.`,
}, { },
{
color: infoColor1, color: infoColor1,
title: "Available API Commands:", title: 'Available API Commands:',
fields: [ fields: [
{ {
name: `\`${config.prefix}api help\``, name: `\`${config.prefix}api help\``,
value: "This command", value: 'This command',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api status\``, name: `\`${config.prefix}api status\``,
value: "Shows the current status of the API for the channel this was run in", value: 'Shows the current status of the API for the channel this was run in',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api allow/enable\``, name: `\`${config.prefix}api allow/enable\``,
value: "Allows API Rolls to be sent to the channel this was run in", value: 'Allows API Rolls to be sent to the channel this was run in',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api block/disable\``, name: `\`${config.prefix}api block/disable\``,
value: "Blocks API Rolls from being sent to the channel this was run in", value: 'Blocks API Rolls from being sent to the channel this was run in',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api delete\``, name: `\`${config.prefix}api delete\``,
value: "Deletes this channel's settings from The Artificer's database", value: 'Deletes this channel\'s settings from The Artificer\'s database',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api show-warn\``, name: `\`${config.prefix}api show-warn\``,
value: "Shows the API warning on all rolls sent to the channel this was run in", value: 'Shows the API warning on all rolls sent to the channel this was run in',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api hide-warn\``, name: `\`${config.prefix}api hide-warn\``,
value: "Hides the API warning on all rolls sent to the channel this was run in", value: 'Hides the API warning on all rolls sent to the channel this was run in',
inline: true inline: true,
} },
] ],
} },
] ],
}, },
apiPermError: { apiPermError: {
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "API commands are powerful and can only be used by guild Owners and Admins.", title: 'API commands are powerful and can only be used by guild Owners and Admins.',
description: "For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer)." description: 'For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer).',
}] }],
}, },
apiRemoveGuild: { apiRemoveGuild: {
embeds: [{ embeds: [{
color: successColor, color: successColor,
title: "This guild's API setting has been removed from The Artifier's Database." title: 'This guild\'s API setting has been removed from The Artifier\'s Database.',
}] }],
}, },
apiStatusFail: { apiStatusFail: {
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "Failed to check API rolls status for this guild.", title: 'Failed to check API rolls status for this guild.',
description: "If this issue persists, please report this to the developers." description: 'If this issue persists, please report this to the developers.',
}] }],
}, },
help: { help: {
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: "The Artificer's Available Commands:", title: 'The Artificer\'s Available Commands:',
fields: [ fields: [
{ {
name: `\`${config.prefix}?\``, name: `\`${config.prefix}?\``,
value: "This command", value: 'This command',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}rollhelp\` or \`${config.prefix}??\``, name: `\`${config.prefix}rollhelp\` or \`${config.prefix}??\``,
value: `Details on how to use the roll command, listed as \`${config.prefix}xdy...${config.postfix}\` below`, value: `Details on how to use the roll command, listed as \`${config.prefix}xdy...${config.postfix}\` below`,
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}api [subcommand]\``, name: `\`${config.prefix}api [subcommand]\``,
value: `Administrative tools for the bots's API, run \`${config.prefix}api help\` for more details`, value: `Administrative tools for the bots's API, run \`${config.prefix}api help\` for more details`,
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}ping\``, name: `\`${config.prefix}ping\``,
value: "Pings the bot to check connectivity", value: 'Pings the bot to check connectivity',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}info\``, name: `\`${config.prefix}info\``,
value: "Prints some information and links relating to the bot", value: 'Prints some information and links relating to the bot',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}privacy\``, name: `\`${config.prefix}privacy\``,
value: "Prints some information about the Privacy Policy", value: 'Prints some information about the Privacy Policy',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}version\``, name: `\`${config.prefix}version\``,
value: "Prints the bots version", value: 'Prints the bots version',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}popcat\``, name: `\`${config.prefix}popcat\``,
value: "Popcat", value: 'Popcat',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}report [text]\``, name: `\`${config.prefix}report [text]\``,
value: "Report a command that failed to run", value: 'Report a command that failed to run',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}stats\``, name: `\`${config.prefix}stats\``,
value: "Statistics on the bot", value: 'Statistics on the bot',
inline: true inline: true,
}, { },
{
name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`, name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`,
value: `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`), run \`${config.prefix}??\` for more details`, value:
inline: true `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`), run \`${config.prefix}??\` for more details`,
} inline: true,
] },
}] ],
}],
}, },
indev: { indev: {
embeds: [{ embeds: [{
color: warnColor, color: warnColor,
title: "Command is in development, please try again later." title: 'Command is in development, please try again later.',
}] }],
}, },
info: { info: {
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: "The Artificer, a Discord bot that specializing in rolling dice and calculating math", title: 'The Artificer, a Discord bot that specializing in rolling dice and calculating math',
description: `The Artificer is developed by Ean AKA Burn_E99. description: `The Artificer is developed by Ean AKA Burn_E99.
Additional information can be found on my website [here](https://discord.burne99.com/TheArtificer/). Additional information can be found on my website [here](https://discord.burne99.com/TheArtificer/).
Want to check out my source code? Check it out [here](https://github.com/Burn-E99/TheArtificer). Want to check out my source code? Check it out [here](https://github.com/Burn-E99/TheArtificer).
Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).` Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).`,
}] }],
}, },
loadingStats: { loadingStats: {
embeds: [{ embeds: [{
color: warnColor, color: warnColor,
title: "Compiling latest statistics . . ." title: 'Compiling latest statistics . . .',
}] }],
}, },
mention: { mention: {
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: `Hello! I am ${config.name}!`, title: `Hello! I am ${config.name}!`,
fields: [{ fields: [{
name: "I am a bot that specializes in rolling dice and doing basic algebra", name: 'I am a bot that specializes in rolling dice and doing basic algebra',
value: `To learn about my available commands, please run \`${config.prefix}help\`` value: `To learn about my available commands, please run \`${config.prefix}help\``,
}] }],
}] }],
}, },
privacy: { privacy: {
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: "Privacy Policy", title: 'Privacy Policy',
fields: [{ fields: [{
name: "The Artificer does not track or collect user information via Discord.", name: 'The Artificer does not track or collect user information via Discord.',
value: `The only user submitted information that is stored is submitted via the \`${config.prefix}report\` command. This information is only stored for a short period of time in a location that only the Developer of The Artificer can see. value:
`The only user submitted information that is stored is submitted via the \`${config.prefix}report\` command. This information is only stored for a short period of time in a location that only the Developer of The Artificer can see.
For more details, please check out the Privacy Policy on the GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/PRIVACY.md). For more details, please check out the Privacy Policy on the GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/PRIVACY.md).
Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/TERMS.md).` Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/TERMS.md).`,
}] }],
}] }],
}, },
report: { report: {
embeds: [{ embeds: [{
color: successColor, color: successColor,
title: "Failed command has been reported to my developer.", title: 'Failed command has been reported to my developer.',
description: `For more in depth support, and information about planned maintenance, please join the support server [here](https://discord.gg/peHASXMZYv).` description: `For more in depth support, and information about planned maintenance, please join the support server [here](https://discord.gg/peHASXMZYv).`,
}] }],
}, },
reportFail: { reportFail: {
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "Please provide a short description of what failed", title: 'Please provide a short description of what failed',
description: "Providing a short description helps my developer quickly diagnose what went wrong." description: 'Providing a short description helps my developer quickly diagnose what went wrong.',
}] }],
}, },
rip: { rip: {
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: "The Artificer was built in memory of my Grandmother, Babka", title: 'The Artificer was built in memory of my Grandmother, Babka',
description: `With much love, Ean description: `With much love, Ean
December 21, 2020` December 21, 2020`,
}] }],
}, },
rollHelp: { rollHelp: {
embeds: [ embeds: [
{ {
color: infoColor2, color: infoColor2,
title: "The Artificer's Roll Command Details:", title: 'The Artificer\'s Roll Command Details:',
description: `You can chain as many of these options as you want, as long as the option does not disallow it. description: `You can chain as many of these options as you want, as long as the option does not disallow it.
This command also can fully solve math equations with parenthesis. This command also can fully solve math equations with parenthesis.
@ -223,273 +243,314 @@ Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/
fields: [ fields: [
{ {
name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`, name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`,
value: `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`)` value: `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`)`,
}, { },
name: "`x` [Optional]", {
value: "Number of dice to roll, if omitted, 1 is used", name: '`x` [Optional]',
inline: true value: 'Number of dice to roll, if omitted, 1 is used',
}, { inline: true,
name: "`dy` [Required]", },
value: "Size of dice to roll, `d20` = 20 sided die", {
inline: true name: '`dy` [Required]',
}, { value: 'Size of dice to roll, `d20` = 20 sided die',
name: "`dz` or `dlz` [Optional]", inline: true,
value: "Drops the lowest `z` dice, cannot be used with `kz`", },
inline: true {
}, { name: '`dz` or `dlz` [Optional]',
name: "`kz` or `khz` [Optional]", value: 'Drops the lowest `z` dice, cannot be used with `kz`',
value: "Keeps the highest `z` dice, cannot be used with `dz`", inline: true,
inline: true },
}, { {
name: "`dhz` [Optional]", name: '`kz` or `khz` [Optional]',
value: "Drops the highest `z` dice, cannot be used with `kz`", value: 'Keeps the highest `z` dice, cannot be used with `dz`',
inline: true inline: true,
}, { },
name: "`klz` [Optional]", {
value: "Keeps the lowest `z` dice, cannot be used with `dz`", name: '`dhz` [Optional]',
inline: true value: 'Drops the highest `z` dice, cannot be used with `kz`',
}, { inline: true,
name: "`ra` or `r=q` [Optional]", },
value: "Rerolls any rolls that match `a`, `r3` will reroll every die that land on 3, throwing out old rolls, cannot be used with `ro`", {
inline: true name: '`klz` [Optional]',
}, { value: 'Keeps the lowest `z` dice, cannot be used with `dz`',
name: "`r<q` [Optional]", inline: true,
value: "Rerolls any rolls that are less than or equal to `a`, `r3` will reroll every die that land on 3, 2, or 1, throwing out old rolls, cannot be used with `ro`", },
inline: true {
}, { name: '`ra` or `r=q` [Optional]',
name: "`r>q` [Optional]", value: 'Rerolls any rolls that match `a`, `r3` will reroll every die that land on 3, throwing out old rolls, cannot be used with `ro`',
value: "Rerolls any rolls that are greater than or equal to `a`, `r3` will reroll every die that land on 3 or greater, throwing out old rolls, cannot be used with `ro`", inline: true,
inline: true },
}, { {
name: "`roa` or `ro=q` [Optional]", name: '`r<q` [Optional]',
value: "Rerolls any rolls that match `a`, `ro3` will reroll each die that lands on 3 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`", value: 'Rerolls any rolls that are less than or equal to `a`, `r3` will reroll every die that land on 3, 2, or 1, throwing out old rolls, cannot be used with `ro`',
inline: true inline: true,
}, { },
name: "`ro<q` [Optional]", {
value: "Rerolls any rolls that are less than or equal to `a`, `ro3` will reroll each die that lands on 3, 2, or 1 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`", name: '`r>q` [Optional]',
inline: true value: 'Rerolls any rolls that are greater than or equal to `a`, `r3` will reroll every die that land on 3 or greater, throwing out old rolls, cannot be used with `ro`',
}, { inline: true,
name: "`ro>q` [Optional]", },
value: "Rerolls any rolls that are greater than or equal to `a`, `ro3` will reroll each die that lands on 3 or greater ONLY ONE TIME, throwing out old rolls, cannot be used with `r`", {
inline: true name: '`roa` or `ro=q` [Optional]',
}, { value: 'Rerolls any rolls that match `a`, `ro3` will reroll each die that lands on 3 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`',
name: "`csq` or `cs=q` [Optional]", inline: true,
value: "Changes crit score to `q`", },
inline: true {
}, { name: '`ro<q` [Optional]',
name: "`cs<q` [Optional]", value: 'Rerolls any rolls that are less than or equal to `a`, `ro3` will reroll each die that lands on 3, 2, or 1 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`',
value: "Changes crit score to be less than or equal to `q`", inline: true,
inline: true },
}, { {
name: "`cs>q` [Optional]", name: '`ro>q` [Optional]',
value: "Changes crit score to be greater than or equal to `q`", value: 'Rerolls any rolls that are greater than or equal to `a`, `ro3` will reroll each die that lands on 3 or greater ONLY ONE TIME, throwing out old rolls, cannot be used with `r`',
inline: true inline: true,
}, { },
name: "`cfq` or `cf=q` [Optional]", {
value: "Changes crit fail to `q`", name: '`csq` or `cs=q` [Optional]',
inline: true value: 'Changes crit score to `q`',
}, { inline: true,
name: "`cf<q` [Optional]", },
value: "Changes crit fail to be less than or equal to `q`", {
inline: true name: '`cs<q` [Optional]',
}, { value: 'Changes crit score to be less than or equal to `q`',
name: "`cf>q` [Optional]", inline: true,
value: "Changes crit fail to be greater than or equal to `q`", },
inline: true {
}, { name: '`cs>q` [Optional]',
name: "`!` [Optional]", value: 'Changes crit score to be greater than or equal to `q`',
value: "Exploding, rolls another `dy` for every crit success", inline: true,
inline: true },
}, { {
name: "`!o` [Optional]", name: '`cfq` or `cf=q` [Optional]',
value: "Exploding Once, rolls one `dy` for each original crit success", value: 'Changes crit fail to `q`',
inline: true inline: true,
}, { },
name: "`!=u` [Optional]", {
value: "Explode on `u`, rolls another `dy` for every die that lands on `u`", name: '`cf<q` [Optional]',
inline: true value: 'Changes crit fail to be less than or equal to `q`',
}, { inline: true,
name: "`!>u` [Optional]", },
value: "Explode on `u` and greater, rolls another `dy` for every die that lands on `u` or greater", {
inline: true name: '`cf>q` [Optional]',
}, { value: 'Changes crit fail to be greater than or equal to `q`',
name: "`!<u` [Optional]", inline: true,
value: "Explode on `u` and under, rolls another `dy` for every die that lands on `u` or less", },
inline: true {
}, { name: '`!` [Optional]',
name: "`!o=u` [Optional]", value: 'Exploding, rolls another `dy` for every crit success',
value: "Explodes Once on `u`, rolls another `dy` for each original die that landed on `u`", inline: true,
inline: true },
} {
] name: '`!o` [Optional]',
}, { value: 'Exploding Once, rolls one `dy` for each original crit success',
inline: true,
},
{
name: '`!=u` [Optional]',
value: 'Explode on `u`, rolls another `dy` for every die that lands on `u`',
inline: true,
},
{
name: '`!>u` [Optional]',
value: 'Explode on `u` and greater, rolls another `dy` for every die that lands on `u` or greater',
inline: true,
},
{
name: '`!<u` [Optional]',
value: 'Explode on `u` and under, rolls another `dy` for every die that lands on `u` or less',
inline: true,
},
{
name: '`!o=u` [Optional]',
value: 'Explodes Once on `u`, rolls another `dy` for each original die that landed on `u`',
inline: true,
},
],
},
{
color: infoColor2, color: infoColor2,
fields: [ fields: [
{ {
name: "`!o>u` [Optional]", name: '`!o>u` [Optional]',
value: "Explode Once on `u` and greater, rolls another `dy` for each original die that landed on `u` or greater", value: 'Explode Once on `u` and greater, rolls another `dy` for each original die that landed on `u` or greater',
inline: true inline: true,
}, { },
name: "`!o<u` [Optional]", {
value: "Explode Once on `u` and under, rolls another `dy` for each original die that landed on `u` or less", name: '`!o<u` [Optional]',
inline: true value: 'Explode Once on `u` and under, rolls another `dy` for each original die that landed on `u` or less',
} inline: true,
] },
}, { ],
},
{
color: infoColor1, color: infoColor1,
title: "Roll Command Decorators:", title: 'Roll Command Decorators:',
description: `This command also has some useful decorators that can used. These decorators simply need to be placed after all rolls in the message. description: `This command also has some useful decorators that can used. These decorators simply need to be placed after all rolls in the message.
Examples: \`${config.prefix}d20${config.postfix} -nd\`, \`${config.prefix}d20${config.postfix} -nd -s\`, \`${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} -o a\``, Examples: \`${config.prefix}d20${config.postfix} -nd\`, \`${config.prefix}d20${config.postfix} -nd -s\`, \`${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} -o a\``,
fields: [ fields: [
{ {
name: "`-nd` - No Details", name: '`-nd` - No Details',
value: "Suppresses all details of the requested roll", value: 'Suppresses all details of the requested roll',
inline: true inline: true,
}, { },
name: "`-snd` - Super No Details", {
value: "Suppresses all details of the requested roll and hides no details message", name: '`-snd` - Super No Details',
inline: true value: 'Suppresses all details of the requested roll and hides no details message',
}, { inline: true,
name: "`-s` - Spoiler", },
value: "Spoilers all details of the requested roll", {
inline: true name: '`-s` - Spoiler',
}, { value: 'Spoilers all details of the requested roll',
name: "`-m` - Maximize Roll", inline: true,
value: "Rolls the theoretical maximum roll, cannot be used with -n", },
inline: true {
}, { name: '`-m` - Maximize Roll',
name: "`-n` - Nominal Roll", value: 'Rolls the theoretical maximum roll, cannot be used with -n',
value: "Rolls the theoretical nominal roll, cannot be used with -m", inline: true,
inline: true },
}, { {
name: "`-gm @user1 @user2 @usern` - GM Roll", name: '`-n` - Nominal Roll',
value: "Rolls the requested roll in GM mode, suppressing all publicly shown results and details and sending the results directly to the specified GMs", value: 'Rolls the theoretical nominal roll, cannot be used with -m',
inline: true inline: true,
}, { },
name: "`-c` - Count Rolls", {
value: "Shows the Count Embed, containing the count of successful rolls, failed rolls, rerolls, drops, and explosions", name: '`-gm @user1 @user2 @usern` - GM Roll',
inline: true value: 'Rolls the requested roll in GM mode, suppressing all publicly shown results and details and sending the results directly to the specified GMs',
}, { inline: true,
name: "`-o [direction]` - Order Roll", },
{
name: '`-c` - Count Rolls',
value: 'Shows the Count Embed, containing the count of successful rolls, failed rolls, rerolls, drops, and explosions',
inline: true,
},
{
name: '`-o [direction]` - Order Roll',
value: `Rolls the requested roll and orders the results in the requested direction value: `Rolls the requested roll and orders the results in the requested direction
Available directions: Available directions:
\`a\` - Ascending (least to greatest) \`a\` - Ascending (least to greatest)
\`d\` - Descending (greatest to least)`, \`d\` - Descending (greatest to least)`,
inline: true inline: true,
} },
] ],
}, { },
{
color: successColor, color: successColor,
title: "Results Formatting:", title: 'Results Formatting:',
description: "The results have some formatting applied on them to provide details on what happened during this roll.", description: 'The results have some formatting applied on them to provide details on what happened during this roll.',
fields: [ fields: [
{ {
name: "Bold", name: 'Bold',
value: "Critical successes will be **bolded**.", value: 'Critical successes will be **bolded**.',
inline: true inline: true,
}, { },
name: "Underline", {
value: "Critical fails will be __underlined__.", name: 'Underline',
inline: true value: 'Critical fails will be __underlined__.',
}, { inline: true,
name: "Strikethrough", },
value: "Rolls that were dropped or rerolled ~~crossed out~~.", {
inline: true name: 'Strikethrough',
} value: 'Rolls that were dropped or rerolled ~~crossed out~~.',
] inline: true,
} },
] ],
},
],
}, },
rolling: { rolling: {
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: "Rolling . . ." title: 'Rolling . . .',
}] }],
}, },
version: { version: {
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: `My current version is ${config.version}` title: `My current version is ${config.version}`,
}] }],
} },
}; };
export const generatePing = (time: number) => ({ export const generatePing = (time: number) => ({
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: time === -1 ? "Ping?" : `Pong! Latency is ${time}ms.` title: time === -1 ? 'Ping?' : `Pong! Latency is ${time}ms.`,
}] }],
}); });
export const generateReport = (msg: string) => ({ export const generateReport = (msg: string) => ({
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: "USER REPORT:", title: 'USER REPORT:',
description: msg || "No message" description: msg || 'No message',
}] }],
}); });
export const generateStats = (guildCount: number, channelCount: number, memberCount: number, rollCount: bigint, utilityCount: bigint) => ({ export const generateStats = (guildCount: number, channelCount: number, memberCount: number, rollCount: bigint, utilityCount: bigint) => ({
embeds: [{ embeds: [{
color: infoColor2, color: infoColor2,
title: "The Artificer's Statistics:", title: 'The Artificer\'s Statistics:',
fields: [ fields: [
{ {
name: "Guilds:", name: 'Guilds:',
value: `${guildCount}`, value: `${guildCount}`,
inline: true inline: true,
}, { },
name: "Channels:", {
name: 'Channels:',
value: `${channelCount}`, value: `${channelCount}`,
inline: true inline: true,
}, { },
name: "Active Members:", {
name: 'Active Members:',
value: `${memberCount}`, value: `${memberCount}`,
inline: true inline: true,
}, { },
name: "Roll Commands:", {
name: 'Roll Commands:',
value: `${rollCount}`, value: `${rollCount}`,
inline: true inline: true,
}, { },
name: "Utility Commands:", {
name: 'Utility Commands:',
value: `${utilityCount}`, value: `${utilityCount}`,
inline: true inline: true,
} },
] ],
}] }],
}); });
export const generateApiFailed = (args: string) => ({ export const generateApiFailed = (args: string) => ({
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: `Failed to ${args} API rolls for this guild.`, title: `Failed to ${args} API rolls for this guild.`,
description: "If this issue persists, please report this to the developers." description: 'If this issue persists, please report this to the developers.',
}] }],
}); });
export const generateApiStatus = (banned: boolean, active: boolean) => ({ export const generateApiStatus = (banned: boolean, active: boolean) => ({
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: `The Artificer's API is ${config.api.enable ? "currently enabled" : "currently disabled"}.`, title: `The Artificer's API is ${config.api.enable ? 'currently enabled' : 'currently disabled'}.`,
description: banned ? "API rolls are banned from being used in this guild.\n\nThis will not be reversed." : `API rolls are ${active ? "allowed" : "blocked from being used"} in this guild.` description: banned ? 'API rolls are banned from being used in this guild.\n\nThis will not be reversed.' : `API rolls are ${active ? 'allowed' : 'blocked from being used'} in this guild.`,
}] }],
}); });
export const generateApiSuccess = (args: string) => ({ export const generateApiSuccess = (args: string) => ({
embeds: [{ embeds: [{
color: successColor, color: successColor,
title: `API rolls have successfully been ${args} for this guild.` title: `API rolls have successfully been ${args} for this guild.`,
}] }],
}); });
export const generateDMFailed = (user: string) => ({ export const generateDMFailed = (user: string) => ({
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: `WARNING: ${user} could not be messaged.`, title: `WARNING: ${user} could not be messaged.`,
description: "If this issue persists, make sure direct messages are allowed from this server." description: 'If this issue persists, make sure direct messages are allowed from this server.',
}] }],
}); });
export const generateApiKeyEmail = (email: string, key: string) => ({ export const generateApiKeyEmail = (email: string, key: string) => ({
@ -498,13 +559,15 @@ export const generateApiKeyEmail = (email: string, key: string) => ({
color: infoColor1, color: infoColor1,
fields: [ fields: [
{ {
name: "Send to:", name: 'Send to:',
value: email value: email,
}, { },
name: "Subject:", {
value: "Artificer API Key" name: 'Subject:',
}, { value: 'Artificer API Key',
name: "Body:", },
{
name: 'Body:',
value: `Hello Artificer API User, value: `Hello Artificer API User,
Welcome aboard The Artificer's API. You can find full details about the API on the GitHub: https://github.com/Burn-E99/TheArtificer Welcome aboard The Artificer's API. You can find full details about the API on the GitHub: https://github.com/Burn-E99/TheArtificer
@ -514,10 +577,10 @@ Your API Key is: ${key}
Guard this well, as there is zero tolerance for API abuse. Guard this well, as there is zero tolerance for API abuse.
Welcome aboard, Welcome aboard,
The Artificer Developer - Ean Milligan` The Artificer Developer - Ean Milligan`,
} },
] ],
}] }],
}); });
export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({ export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({
@ -526,13 +589,15 @@ export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({
color: infoColor1, color: infoColor1,
fields: [ fields: [
{ {
name: "Send to:", name: 'Send to:',
value: email value: email,
}, { },
name: "Subject:", {
value: "Artificer API Delete Code" name: 'Subject:',
}, { value: 'Artificer API Delete Code',
name: "Body:", },
{
name: 'Body:',
value: `Hello Artificer API User, value: `Hello Artificer API User,
I am sorry to see you go. If you would like, please respond to this email detailing what I could have done better. I am sorry to see you go. If you would like, please respond to this email detailing what I could have done better.
@ -540,53 +605,58 @@ I am sorry to see you go. If you would like, please respond to this email detai
As requested, here is your delete code: ${deleteCode} As requested, here is your delete code: ${deleteCode}
Sorry to see you go, Sorry to see you go,
The Artificer Developer - Ean Milligan` The Artificer Developer - Ean Milligan`,
} },
] ],
}] }],
}); });
export const generateRollError = (errorType: string, errorMsg: string) => ({ export const generateRollError = (errorType: string, errorMsg: string) => ({
embeds: [{ embeds: [{
color: failColor, color: failColor,
title: "Roll command encountered the following error:", title: 'Roll command encountered the following error:',
fields: [{ fields: [{
name: errorType, name: errorType,
value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.` value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.`,
}] }],
}] }],
}); });
export const generateCountDetails = (counts: CountDetails) => ({ export const generateCountDetails = (counts: CountDetails) => ({
embeds: [{ embeds: [{
color: infoColor1, color: infoColor1,
title: "Roll Count Details:", title: 'Roll Count Details:',
fields: [ fields: [
{ {
name: "Total Rolls:", name: 'Total Rolls:',
details: `${counts.total}`, details: `${counts.total}`,
inline: true inline: true,
}, { },
name: "Successful Rolls:", {
name: 'Successful Rolls:',
details: `${counts.successful}`, details: `${counts.successful}`,
inline: true inline: true,
}, { },
name: "Failed Rolls:", {
name: 'Failed Rolls:',
details: `${counts.failed}`, details: `${counts.failed}`,
inline: true inline: true,
}, { },
name: "Rerolled Dice:", {
name: 'Rerolled Dice:',
details: `${counts.rerolled}`, details: `${counts.rerolled}`,
inline: true inline: true,
}, { },
name: "Dropped Dice:", {
name: 'Dropped Dice:',
details: `${counts.dropped}`, details: `${counts.dropped}`,
inline: true inline: true,
}, { },
name: "Exploded Dice:", {
name: 'Exploded Dice:',
details: `${counts.exploded}`, details: `${counts.exploded}`,
inline: true inline: true,
} },
] ],
}] }],
}); });

View File

@ -1,15 +1,15 @@
import config from "../config.ts"; import config from '../config.ts';
import { Client } from "../deps.ts"; import { Client } from '../deps.ts';
import { LOCALMODE } from "../flags.ts"; import { LOCALMODE } from '../flags.ts';
export const dbClient = await new Client().connect({ export const dbClient = await new Client().connect({
hostname: LOCALMODE ? config.db.localhost : config.db.host, hostname: LOCALMODE ? config.db.localhost : config.db.host,
port: config.db.port, port: config.db.port,
db: config.db.name, db: config.db.name,
username: config.db.username, username: config.db.username,
password: config.db.password password: config.db.password,
}); });
export const queries = { export const queries = {
insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})` insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})`,
}; };

View File

@ -6,18 +6,19 @@
import { import {
// Discordeno deps // Discordeno deps
cache, cacheHandlers, cache,
cacheHandlers,
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../deps.ts"; } from '../deps.ts';
import config from "../config.ts"; import config from '../config.ts';
// getRandomStatus() returns status as string // getRandomStatus() returns status as string
// Gets a new random status for the bot // Gets a new random status for the bot
const getRandomStatus = async (): Promise<string> => { const getRandomStatus = async (): Promise<string> => {
let status = ""; let status = '';
switch (Math.floor((Math.random() * 4) + 1)) { switch (Math.floor((Math.random() * 4) + 1)) {
case 1: case 1:
status = `${config.prefix}help for commands`; status = `${config.prefix}help for commands`;
@ -29,29 +30,29 @@ const getRandomStatus = async (): Promise<string> => {
status = `${config.prefix}info to learn more`; status = `${config.prefix}info to learn more`;
break; break;
default: { default: {
const cachedCount = await cacheHandlers.size("guilds") const cachedCount = await cacheHandlers.size('guilds');
status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`; status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`;
break; break;
} }
} }
return status; return status;
}; };
// updateListStatistics(bot ID, current guild count) returns nothing // updateListStatistics(bot ID, current guild count) returns nothing
// Sends the current server count to all bot list sites we are listed on // Sends the current server count to all bot list sites we are listed on
const updateListStatistics = (botID: bigint, serverCount: number): void => { const updateListStatistics = (botID: bigint, serverCount: number): void => {
config.botLists.forEach(async e => { config.botLists.forEach(async (e) => {
log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`) log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`);
if (e.enabled) { if (e.enabled) {
const tempHeaders = new Headers(); const tempHeaders = new Headers();
tempHeaders.append(e.headers[0].header, e.headers[0].value); tempHeaders.append(e.headers[0].header, e.headers[0].value);
tempHeaders.append("Content-Type", "application/json"); tempHeaders.append('Content-Type', 'application/json');
// ?{} is a template used in config, just need to replace it with the real value // ?{} is a template used in config, just need to replace it with the real value
const response = await fetch(e.apiUrl.replace("?{bot_id}", botID.toString()), { const response = await fetch(e.apiUrl.replace('?{bot_id}', botID.toString()), {
"method": 'POST', 'method': 'POST',
"headers": tempHeaders, 'headers': tempHeaders,
"body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number 'body': JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()), // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number
}); });
log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`);
} }

30
src/mod.d.ts vendored
View File

@ -2,23 +2,23 @@
// EmojiConf is used as a structure for the emojis stored in config.ts // EmojiConf is used as a structure for the emojis stored in config.ts
export type EmojiConf = { export type EmojiConf = {
name: string, name: string;
aliases: Array<string>, aliases: Array<string>;
id: string, id: string;
animated: boolean, animated: boolean;
deleteSender: boolean deleteSender: boolean;
}; };
// RollModifiers is the structure to keep track of the decorators applied to a roll command // RollModifiers is the structure to keep track of the decorators applied to a roll command
export type RollModifiers = { export type RollModifiers = {
noDetails: boolean, noDetails: boolean;
superNoDetails: boolean, superNoDetails: boolean;
spoiler: string, spoiler: string;
maxRoll: boolean, maxRoll: boolean;
nominalRoll: boolean, nominalRoll: boolean;
gmRoll: boolean, gmRoll: boolean;
gms: string[], gms: string[];
order: string, order: string;
valid: boolean, valid: boolean;
count: boolean count: boolean;
}; };

View File

@ -1,5 +1,5 @@
import { parseRoll } from "./parser.ts"; import { parseRoll } from './parser.ts';
export default { export default {
parseRoll parseRoll,
}; };

View File

@ -1,26 +1,27 @@
import { import {
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import config from "../../config.ts"; import config from '../../config.ts';
import { RollModifiers } from "../mod.d.ts"; import { RollModifiers } from '../mod.d.ts';
import { SolvedStep, SolvedRoll, ReturnData } from "./solver.d.ts"; import { ReturnData, SolvedRoll, SolvedStep } from './solver.d.ts';
import { compareTotalRolls, escapeCharacters } from "./rollUtils.ts"; import { compareTotalRolls, escapeCharacters } from './rollUtils.ts';
import { formatRoll } from "./rollFormatter.ts"; import { formatRoll } from './rollFormatter.ts';
import { fullSolver } from "./solver.ts"; import { fullSolver } from './solver.ts';
// parseRoll(fullCmd, modifiers) // parseRoll(fullCmd, modifiers)
// parseRoll handles converting fullCmd into a computer readable format for processing, and finally executes the solving // parseRoll handles converting fullCmd into a computer readable format for processing, and finally executes the solving
export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => {
const returnmsg = { const returnmsg = {
error: false, error: false,
errorMsg: "", errorMsg: '',
errorCode: "", errorCode: '',
line1: "", line1: '',
line2: "", line2: '',
line3: "" line3: '',
}; };
// Whole function lives in a try-catch to allow safe throwing of errors on purpose // Whole function lives in a try-catch to allow safe throwing of errors on purpose
@ -37,22 +38,22 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
const [tempConf, tempFormat] = sepRolls[i].split(config.postfix); const [tempConf, tempFormat] = sepRolls[i].split(config.postfix);
// Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on) // Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on)
const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]>tempConf.replace(/ /g, "").split(/([-+()*/%^])/g); const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]> tempConf.replace(/ /g, '').split(/([-+()*/%^])/g);
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens // Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
let parenCnt = 0; let parenCnt = 0;
mathConf.forEach(e => { mathConf.forEach((e) => {
log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`); log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`);
if (e === "(") { if (e === '(') {
parenCnt++; parenCnt++;
} else if (e === ")") { } else if (e === ')') {
parenCnt--; parenCnt--;
} }
}); });
// If the parenCnt is not 0, then we do not have balanced parens and need to error out now // If the parenCnt is not 0, then we do not have balanced parens and need to error out now
if (parenCnt !== 0) { if (parenCnt !== 0) {
throw new Error("UnbalancedParens"); throw new Error('UnbalancedParens');
} }
// Evaluate all rolls into stepSolve format and all numbers into floats // Evaluate all rolls into stepSolve format and all numbers into floats
@ -68,35 +69,35 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
} else if (/([0123456789])/g.test(mathConf[i].toString())) { } else if (/([0123456789])/g.test(mathConf[i].toString())) {
// If there is a number somewhere in mathconf[i] but there are also other characters preventing it from parsing correctly as a number, it should be a dice roll, parse it as such (if it for some reason is not a dice roll, formatRoll/roll will handle it) // If there is a number somewhere in mathconf[i] but there are also other characters preventing it from parsing correctly as a number, it should be a dice roll, parse it as such (if it for some reason is not a dice roll, formatRoll/roll will handle it)
mathConf[i] = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll); mathConf[i] = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll);
} else if (mathConf[i].toString().toLowerCase() === "e") { } else if (mathConf[i].toString().toLowerCase() === 'e') {
// If the operand is the constant e, create a SolvedStep for it // If the operand is the constant e, create a SolvedStep for it
mathConf[i] = { mathConf[i] = {
total: Math.E, total: Math.E,
details: "*e*", details: '*e*',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}; };
} else if (mathConf[i].toString().toLowerCase() === "pi" || mathConf[i].toString().toLowerCase() == "𝜋") { } else if (mathConf[i].toString().toLowerCase() === 'pi' || mathConf[i].toString().toLowerCase() == '𝜋') {
// If the operand is the constant pi, create a SolvedStep for it // If the operand is the constant pi, create a SolvedStep for it
mathConf[i] = { mathConf[i] = {
total: Math.PI, total: Math.PI,
details: "𝜋", details: '𝜋',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}; };
} else if (mathConf[i].toString().toLowerCase() === "pie") { } else if (mathConf[i].toString().toLowerCase() === 'pie') {
// If the operand is pie, pi*e, create a SolvedStep for e and pi (and the multiplication symbol between them) // If the operand is pie, pi*e, create a SolvedStep for e and pi (and the multiplication symbol between them)
mathConf[i] = { mathConf[i] = {
total: Math.PI, total: Math.PI,
details: "𝜋", details: '𝜋',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}; };
mathConf.splice((i + 1), 0, ...["*", { mathConf.splice(i + 1, 0, ...['*', {
total: Math.E, total: Math.E,
details: "*e*", details: '*e*',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}]); }]);
} }
} }
@ -111,51 +112,51 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
rollDetails: tempSolved.details, rollDetails: tempSolved.details,
containsCrit: tempSolved.containsCrit, containsCrit: tempSolved.containsCrit,
containsFail: tempSolved.containsFail, containsFail: tempSolved.containsFail,
initConfig: tempConf initConfig: tempConf,
}); });
} }
// Parsing/Solving done, time to format the output for Discord // Parsing/Solving done, time to format the output for Discord
// Remove any floating spaces from fullCmd // Remove any floating spaces from fullCmd
if (fullCmd[fullCmd.length - 1] === " ") { if (fullCmd[fullCmd.length - 1] === ' ') {
fullCmd = fullCmd.substring(0, (fullCmd.length - 1)); fullCmd = fullCmd.substring(0, fullCmd.length - 1);
} }
// Escape any | and ` chars in fullCmd to prevent spoilers and code blocks from acting up // Escape any | and ` chars in fullCmd to prevent spoilers and code blocks from acting up
fullCmd = escapeCharacters(fullCmd, "|"); fullCmd = escapeCharacters(fullCmd, '|');
fullCmd = fullCmd.replace(/`/g, ""); fullCmd = fullCmd.replace(/`/g, '');
let line1 = ""; let line1 = '';
let line2 = ""; let line2 = '';
let line3 = ""; let line3 = '';
// If maximiseRoll or nominalRoll are on, mark the output as such, else use default formatting // If maximiseRoll or nominalRoll are on, mark the output as such, else use default formatting
if (modifiers.maxRoll) { if (modifiers.maxRoll) {
line1 = ` requested the theoretical maximum of: \`${config.prefix}${fullCmd}\``; line1 = ` requested the theoretical maximum of: \`${config.prefix}${fullCmd}\``;
line2 = "Theoretical Maximum Results: "; line2 = 'Theoretical Maximum Results: ';
} else if (modifiers.nominalRoll) { } else if (modifiers.nominalRoll) {
line1 = ` requested the theoretical nominal of: \`${config.prefix}${fullCmd}\``; line1 = ` requested the theoretical nominal of: \`${config.prefix}${fullCmd}\``;
line2 = "Theoretical Nominal Results: "; line2 = 'Theoretical Nominal Results: ';
} else if (modifiers.order === "a") { } else if (modifiers.order === 'a') {
line1 = ` requested the following rolls to be ordered from least to greatest: \`${config.prefix}${fullCmd}\``; line1 = ` requested the following rolls to be ordered from least to greatest: \`${config.prefix}${fullCmd}\``;
line2 = "Results: "; line2 = 'Results: ';
tempReturnData.sort(compareTotalRolls); tempReturnData.sort(compareTotalRolls);
} else if (modifiers.order === "d") { } else if (modifiers.order === 'd') {
line1 = ` requested the following rolls to be ordered from greatest to least: \`${config.prefix}${fullCmd}\``; line1 = ` requested the following rolls to be ordered from greatest to least: \`${config.prefix}${fullCmd}\``;
line2 = "Results: "; line2 = 'Results: ';
tempReturnData.sort(compareTotalRolls); tempReturnData.sort(compareTotalRolls);
tempReturnData.reverse(); tempReturnData.reverse();
} else { } else {
line1 = ` rolled: \`${config.prefix}${fullCmd}\``; line1 = ` rolled: \`${config.prefix}${fullCmd}\``;
line2 = "Results: "; line2 = 'Results: ';
} }
// Fill out all of the details and results now // Fill out all of the details and results now
tempReturnData.forEach(e => { tempReturnData.forEach((e) => {
log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`); log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`);
let preFormat = ""; let preFormat = '';
let postFormat = ""; let postFormat = '';
// If the roll containted a crit success or fail, set the formatting around it // If the roll containted a crit success or fail, set the formatting around it
if (e.containsCrit) { if (e.containsCrit) {
@ -168,108 +169,107 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
} }
// Populate line2 (the results) and line3 (the details) with their data // Populate line2 (the results) and line3 (the details) with their data
if (modifiers.order === "") { if (modifiers.order === '') {
line2 += `${preFormat}${e.rollTotal}${postFormat}${escapeCharacters(e.rollPostFormat, "|*_~`")}`; line2 += `${preFormat}${e.rollTotal}${postFormat}${escapeCharacters(e.rollPostFormat, '|*_~`')}`;
} else { } else {
// If order is on, turn rolls into csv without formatting // If order is on, turn rolls into csv without formatting
line2 += `${preFormat}${e.rollTotal}${postFormat}, `; line2 += `${preFormat}${e.rollTotal}${postFormat}, `;
} }
line2 = line2.replace(/\*\*\*\*/g, "** **").replace(/____/g, "__ __").replace(/~~~~/g, "~~ ~~"); line2 = line2.replace(/\*\*\*\*/g, '** **').replace(/____/g, '__ __').replace(/~~~~/g, '~~ ~~');
line3 += `\`${e.initConfig}\` = ${e.rollDetails} = ${preFormat}${e.rollTotal}${postFormat}\n`; line3 += `\`${e.initConfig}\` = ${e.rollDetails} = ${preFormat}${e.rollTotal}${postFormat}\n`;
}); });
// If order is on, remove trailing ", " // If order is on, remove trailing ", "
if (modifiers.order !== "") { if (modifiers.order !== '') {
line2 = line2.substring(0, (line2.length - 2)); line2 = line2.substring(0, line2.length - 2);
} }
// Fill in the return block // Fill in the return block
returnmsg.line1 = line1; returnmsg.line1 = line1;
returnmsg.line2 = line2; returnmsg.line2 = line2;
returnmsg.line3 = line3; returnmsg.line3 = line3;
} catch (solverError) { } catch (solverError) {
// Welp, the unthinkable happened, we hit an error // Welp, the unthinkable happened, we hit an error
// Split on _ for the error messages that have more info than just their name // Split on _ for the error messages that have more info than just their name
const [errorName, errorDetails] = solverError.message.split("_"); const [errorName, errorDetails] = solverError.message.split('_');
let errorMsg = ""; let errorMsg = '';
// Translate the errorName to a specific errorMsg // Translate the errorName to a specific errorMsg
switch (errorName) { switch (errorName) {
case "YouNeedAD": case 'YouNeedAD':
errorMsg = "Formatting Error: Missing die size and count config"; errorMsg = 'Formatting Error: Missing die size and count config';
break; break;
case "FormattingError": case 'FormattingError':
errorMsg = "Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll"; errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll';
break; break;
case "NoMaxWithDash": case 'NoMaxWithDash':
errorMsg = "Formatting Error: CritScore range specified without a maximum, remove - or add maximum to correct"; errorMsg = 'Formatting Error: CritScore range specified without a maximum, remove - or add maximum to correct';
break; break;
case "UnknownOperation": case 'UnknownOperation':
errorMsg = `Error: Unknown Operation ${errorDetails}`; errorMsg = `Error: Unknown Operation ${errorDetails}`;
if (errorDetails === "-") { if (errorDetails === '-') {
errorMsg += "\nNote: Negative numbers are not supported"; errorMsg += '\nNote: Negative numbers are not supported';
} else if (errorDetails === " ") { } else if (errorDetails === ' ') {
errorMsg += `\nNote: Every roll must be closed by ${config.postfix}`; errorMsg += `\nNote: Every roll must be closed by ${config.postfix}`;
} }
break; break;
case "NoZerosAllowed": case 'NoZerosAllowed':
errorMsg = "Formatting Error: "; errorMsg = 'Formatting Error: ';
switch (errorDetails) { switch (errorDetails) {
case "base": case 'base':
errorMsg += "Die Size and Die Count"; errorMsg += 'Die Size and Die Count';
break; break;
case "drop": case 'drop':
errorMsg += "Drop (d or dl)"; errorMsg += 'Drop (d or dl)';
break; break;
case "keep": case 'keep':
errorMsg += "Keep (k or kh)"; errorMsg += 'Keep (k or kh)';
break; break;
case "dropHigh": case 'dropHigh':
errorMsg += "Drop Highest (dh)"; errorMsg += 'Drop Highest (dh)';
break; break;
case "keepLow": case 'keepLow':
errorMsg += "Keep Lowest (kl)"; errorMsg += 'Keep Lowest (kl)';
break; break;
case "reroll": case 'reroll':
errorMsg += "Reroll (r)"; errorMsg += 'Reroll (r)';
break; break;
case "critScore": case 'critScore':
errorMsg += "Crit Score (cs)"; errorMsg += 'Crit Score (cs)';
break; break;
default: default:
errorMsg += `Unhandled - ${errorDetails}`; errorMsg += `Unhandled - ${errorDetails}`;
break; break;
} }
errorMsg += " cannot be zero"; errorMsg += ' cannot be zero';
break; break;
case "CritScoreMinGtrMax": case 'CritScoreMinGtrMax':
errorMsg = "Formatting Error: CritScore maximum cannot be greater than minimum, check formatting and flip min/max"; errorMsg = 'Formatting Error: CritScore maximum cannot be greater than minimum, check formatting and flip min/max';
break; break;
case "MaxLoopsExceeded": case 'MaxLoopsExceeded':
errorMsg = "Error: Roll is too complex or reaches infinity"; errorMsg = 'Error: Roll is too complex or reaches infinity';
break; break;
case "UnbalancedParens": case 'UnbalancedParens':
errorMsg = "Formatting Error: At least one of the equations contains unbalanced parenthesis"; errorMsg = 'Formatting Error: At least one of the equations contains unbalanced parenthesis';
break; break;
case "EMDASNotNumber": case 'EMDASNotNumber':
errorMsg = "Error: One or more operands is not a number"; errorMsg = 'Error: One or more operands is not a number';
break; break;
case "ConfWhat": case 'ConfWhat':
errorMsg = "Error: Not all values got processed, please report the command used"; errorMsg = 'Error: Not all values got processed, please report the command used';
break; break;
case "OperatorWhat": case 'OperatorWhat':
errorMsg = "Error: Something really broke with the Operator, try again"; errorMsg = 'Error: Something really broke with the Operator, try again';
break; break;
case "OperandNaN": case 'OperandNaN':
errorMsg = "Error: One or more operands reached NaN, check input"; errorMsg = 'Error: One or more operands reached NaN, check input';
break; break;
case "UndefinedStep": case 'UndefinedStep':
errorMsg = "Error: Roll became undefined, one or more operands are not a roll or a number, check input"; errorMsg = 'Error: Roll became undefined, one or more operands are not a roll or a number, check input';
break; break;
default: default:
log(LT.ERROR, `Undangled Error: ${errorName}, ${errorDetails}`); log(LT.ERROR, `Undangled Error: ${errorName}, ${errorDetails}`);

View File

@ -1,27 +1,28 @@
import { import {
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { roll } from "./roller.ts"; import { roll } from './roller.ts';
import { SolvedStep } from "./solver.d.ts"; import { SolvedStep } from './solver.d.ts';
// formatRoll(rollConf, maximiseRoll, nominalRoll) returns one SolvedStep // formatRoll(rollConf, maximiseRoll, nominalRoll) returns one SolvedStep
// formatRoll handles creating and formatting the completed rolls into the SolvedStep format // formatRoll handles creating and formatting the completed rolls into the SolvedStep format
export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: boolean): SolvedStep => { export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: boolean): SolvedStep => {
let tempTotal = 0; let tempTotal = 0;
let tempDetails = "["; let tempDetails = '[';
let tempCrit = false; let tempCrit = false;
let tempFail = false; let tempFail = false;
// Generate the roll, passing flags thru // Generate the roll, passing flags thru
const tempRollSet = roll(rollConf, maximiseRoll, nominalRoll); const tempRollSet = roll(rollConf, maximiseRoll, nominalRoll);
// Loop thru all parts of the roll to document everything that was done to create the total roll // Loop thru all parts of the roll to document everything that was done to create the total roll
tempRollSet.forEach(e => { tempRollSet.forEach((e) => {
log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`); log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`);
let preFormat = ""; let preFormat = '';
let postFormat = ""; let postFormat = '';
if (!e.dropped && !e.rerolled) { if (!e.dropped && !e.rerolled) {
// If the roll was not dropped or rerolled, add it to the stepTotal and flag the critHit/critFail // If the roll was not dropped or rerolled, add it to the stepTotal and flag the critHit/critFail
@ -54,13 +55,13 @@ export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll:
tempDetails += `${preFormat}${e.roll}${postFormat} + `; tempDetails += `${preFormat}${e.roll}${postFormat} + `;
}); });
// After the looping is done, remove the extra " + " from the details and cap it with the closing ] // After the looping is done, remove the extra " + " from the details and cap it with the closing ]
tempDetails = tempDetails.substring(0, (tempDetails.length - 3)); tempDetails = tempDetails.substring(0, tempDetails.length - 3);
tempDetails += "]"; tempDetails += ']';
return { return {
total: tempTotal, total: tempTotal,
details: tempDetails, details: tempDetails,
containsCrit: tempCrit, containsCrit: tempCrit,
containsFail: tempFail containsFail: tempFail,
}; };
}; };

View File

@ -1,9 +1,10 @@
import { import {
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { RollSet, ReturnData } from "./solver.d.ts"; import { ReturnData, RollSet } from './solver.d.ts';
// MAXLOOPS determines how long the bot will attempt a roll // MAXLOOPS determines how long the bot will attempt a roll
// Default is 5000000 (5 million), which results in at most a 10 second delay before the bot calls the roll infinite or too complex // Default is 5000000 (5 million), which results in at most a 10 second delay before the bot calls the roll infinite or too complex
@ -60,7 +61,7 @@ export const escapeCharacters = (str: string, esc: string): string => {
for (let i = 0; i < esc.length; i++) { for (let i = 0; i < esc.length; i++) {
log(LT.LOG, `Escaping character ${esc[i]} | ${str}, ${esc}`); log(LT.LOG, `Escaping character ${esc[i]} | ${str}, ${esc}`);
// Create a new regex to look for that char that needs replaced and escape it // Create a new regex to look for that char that needs replaced and escape it
const temprgx = new RegExp(`[${esc[i]}]`, "g"); const temprgx = new RegExp(`[${esc[i]}]`, 'g');
str = str.replace(temprgx, `\\${esc[i]}`); str = str.replace(temprgx, `\\${esc[i]}`);
} }
return str; return str;

View File

@ -1,10 +1,11 @@
import { import {
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { RollSet } from "./solver.d.ts"; import { RollSet } from './solver.d.ts';
import { MAXLOOPS, genRoll, compareRolls, compareOrigidx } from "./rollUtils.ts"; import { compareOrigidx, compareRolls, genRoll, MAXLOOPS } from './rollUtils.ts';
// roll(rollStr, maximiseRoll, nominalRoll) returns RollSet // roll(rollStr, maximiseRoll, nominalRoll) returns RollSet
// roll parses and executes the rollStr, if needed it will also make the roll the maximum or average // roll parses and executes the rollStr, if needed it will also make the roll the maximum or average
@ -19,7 +20,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollStr = rollStr.toLowerCase(); rollStr = rollStr.toLowerCase();
// Split the roll on the die size (and the drop if its there) // Split the roll on the die size (and the drop if its there)
const dpts = rollStr.split("d"); const dpts = rollStr.split('d');
// Initialize the configuration to store the parsed data // Initialize the configuration to store the parsed data
const rollConf = { const rollConf = {
@ -27,47 +28,47 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
dieSize: 0, dieSize: 0,
drop: { drop: {
on: false, on: false,
count: 0 count: 0,
}, },
keep: { keep: {
on: false, on: false,
count: 0 count: 0,
}, },
dropHigh: { dropHigh: {
on: false, on: false,
count: 0 count: 0,
}, },
keepLow: { keepLow: {
on: false, on: false,
count: 0 count: 0,
}, },
reroll: { reroll: {
on: false, on: false,
once: false, once: false,
nums: <number[]>[] nums: <number[]> [],
}, },
critScore: { critScore: {
on: false, on: false,
range: <number[]>[] range: <number[]> [],
}, },
critFail: { critFail: {
on: false, on: false,
range: <number[]>[] range: <number[]> [],
}, },
exploding: { exploding: {
on: false, on: false,
once: false, once: false,
nums: <number[]>[] nums: <number[]> [],
} },
}; };
// If the dpts is not long enough, throw error // If the dpts is not long enough, throw error
if (dpts.length < 2) { if (dpts.length < 2) {
throw new Error("YouNeedAD"); throw new Error('YouNeedAD');
} }
// Fill out the die count, first item will either be an int or empty string, short circuit execution will take care of replacing the empty string with a 1 // Fill out the die count, first item will either be an int or empty string, short circuit execution will take care of replacing the empty string with a 1
const tempDC = (dpts.shift() || "1").replace(/\D/g,''); const tempDC = (dpts.shift() || '1').replace(/\D/g, '');
rollConf.dieCount = parseInt(tempDC); rollConf.dieCount = parseInt(tempDC);
// Finds the end of the die size/beginnning of the additional options // Finds the end of the die size/beginnning of the additional options
@ -77,7 +78,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
} }
// Rejoin all remaining parts // Rejoin all remaining parts
let remains = dpts.join("d"); let remains = dpts.join('d');
// Get the die size out of the remains and into the rollConf // Get the die size out of the remains and into the rollConf
rollConf.dieSize = parseInt(remains.slice(0, afterDieIdx)); rollConf.dieSize = parseInt(remains.slice(0, afterDieIdx));
remains = remains.slice(afterDieIdx); remains = remains.slice(afterDieIdx);
@ -88,7 +89,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
// Finish parsing the roll // Finish parsing the roll
if (remains.length > 0) { if (remains.length > 0) {
// Determine if the first item is a drop, and if it is, add the d back in // Determine if the first item is a drop, and if it is, add the d back in
if (remains.search(/\D/) !== 0 || remains.indexOf("l") === 0 || remains.indexOf("h") === 0) { if (remains.search(/\D/) !== 0 || remains.indexOf('l') === 0 || remains.indexOf('h') === 0) {
remains = `d${remains}`; remains = `d${remains}`;
} }
@ -113,42 +114,42 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
// Switch on rule name // Switch on rule name
switch (tSep) { switch (tSep) {
case "dl": case 'dl':
case "d": case 'd':
// Configure Drop (Lowest) // Configure Drop (Lowest)
rollConf.drop.on = true; rollConf.drop.on = true;
rollConf.drop.count = tNum; rollConf.drop.count = tNum;
break; break;
case "kh": case 'kh':
case "k": case 'k':
// Configure Keep (Highest) // Configure Keep (Highest)
rollConf.keep.on = true; rollConf.keep.on = true;
rollConf.keep.count = tNum; rollConf.keep.count = tNum;
break; break;
case "dh": case 'dh':
// Configure Drop (Highest) // Configure Drop (Highest)
rollConf.dropHigh.on = true; rollConf.dropHigh.on = true;
rollConf.dropHigh.count = tNum; rollConf.dropHigh.count = tNum;
break; break;
case "kl": case 'kl':
// Configure Keep (Lowest) // Configure Keep (Lowest)
rollConf.keepLow.on = true; rollConf.keepLow.on = true;
rollConf.keepLow.count = tNum; rollConf.keepLow.count = tNum;
break; break;
case "ro": case 'ro':
case "ro=": case 'ro=':
rollConf.reroll.once = true; rollConf.reroll.once = true;
// falls through as ro/ro= functions the same as r/r= in this context // falls through as ro/ro= functions the same as r/r= in this context
case "r": case 'r':
case "r=": case 'r=':
// Configure Reroll (this can happen multiple times) // Configure Reroll (this can happen multiple times)
rollConf.reroll.on = true; rollConf.reroll.on = true;
rollConf.reroll.nums.push(tNum); rollConf.reroll.nums.push(tNum);
break; break;
case "ro>": case 'ro>':
rollConf.reroll.once = true; rollConf.reroll.once = true;
// falls through as ro> functions the same as r> in this context // falls through as ro> functions the same as r> in this context
case "r>": case 'r>':
// Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why) // Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why)
rollConf.reroll.on = true; rollConf.reroll.on = true;
for (let i = tNum; i <= rollConf.dieSize; i++) { for (let i = tNum; i <= rollConf.dieSize; i++) {
@ -156,10 +157,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.reroll.nums.push(i); rollConf.reroll.nums.push(i);
} }
break; break;
case "ro<": case 'ro<':
rollConf.reroll.once = true; rollConf.reroll.once = true;
// falls through as ro< functions the same as r< in this context // falls through as ro< functions the same as r< in this context
case "r<": case 'r<':
// Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why) // Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why)
rollConf.reroll.on = true; rollConf.reroll.on = true;
for (let i = 1; i <= tNum; i++) { for (let i = 1; i <= tNum; i++) {
@ -167,13 +168,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.reroll.nums.push(i); rollConf.reroll.nums.push(i);
} }
break; break;
case "cs": case 'cs':
case "cs=": case 'cs=':
// Configure CritScore for one number (this can happen multiple times) // Configure CritScore for one number (this can happen multiple times)
rollConf.critScore.on = true; rollConf.critScore.on = true;
rollConf.critScore.range.push(tNum); rollConf.critScore.range.push(tNum);
break; break;
case "cs>": case 'cs>':
// Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why) // Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why)
rollConf.critScore.on = true; rollConf.critScore.on = true;
for (let i = tNum; i <= rollConf.dieSize; i++) { for (let i = tNum; i <= rollConf.dieSize; i++) {
@ -181,7 +182,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.critScore.range.push(i); rollConf.critScore.range.push(i);
} }
break; break;
case "cs<": case 'cs<':
// Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why) // Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why)
rollConf.critScore.on = true; rollConf.critScore.on = true;
for (let i = 0; i <= tNum; i++) { for (let i = 0; i <= tNum; i++) {
@ -189,13 +190,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.critScore.range.push(i); rollConf.critScore.range.push(i);
} }
break; break;
case "cf": case 'cf':
case "cf=": case 'cf=':
// Configure CritFail for one number (this can happen multiple times) // Configure CritFail for one number (this can happen multiple times)
rollConf.critFail.on = true; rollConf.critFail.on = true;
rollConf.critFail.range.push(tNum); rollConf.critFail.range.push(tNum);
break; break;
case "cf>": case 'cf>':
// Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why) // Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why)
rollConf.critFail.on = true; rollConf.critFail.on = true;
for (let i = tNum; i <= rollConf.dieSize; i++) { for (let i = tNum; i <= rollConf.dieSize; i++) {
@ -203,7 +204,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.critFail.range.push(i); rollConf.critFail.range.push(i);
} }
break; break;
case "cf<": case 'cf<':
// Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why) // Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why)
rollConf.critFail.on = true; rollConf.critFail.on = true;
for (let i = 0; i <= tNum; i++) { for (let i = 0; i <= tNum; i++) {
@ -211,10 +212,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.critFail.range.push(i); rollConf.critFail.range.push(i);
} }
break; break;
case "!o": case '!o':
rollConf.exploding.once = true; rollConf.exploding.once = true;
// falls through as !o functions the same as ! in this context // falls through as !o functions the same as ! in this context
case "!": case '!':
// Configure Exploding // Configure Exploding
rollConf.exploding.on = true; rollConf.exploding.on = true;
if (afterNumIdx > 0) { if (afterNumIdx > 0) {
@ -225,18 +226,18 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
afterNumIdx = 1; afterNumIdx = 1;
} }
break; break;
case "!o=": case '!o=':
rollConf.exploding.once = true; rollConf.exploding.once = true;
// falls through as !o= functions the same as != in this context // falls through as !o= functions the same as != in this context
case "!=": case '!=':
// Configure Exploding (this can happen multiple times) // Configure Exploding (this can happen multiple times)
rollConf.exploding.on = true; rollConf.exploding.on = true;
rollConf.exploding.nums.push(tNum); rollConf.exploding.nums.push(tNum);
break; break;
case "!o>": case '!o>':
rollConf.exploding.once = true; rollConf.exploding.once = true;
// falls through as !o> functions the same as !> in this context // falls through as !o> functions the same as !> in this context
case "!>": case '!>':
// Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why) // Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why)
rollConf.exploding.on = true; rollConf.exploding.on = true;
for (let i = tNum; i <= rollConf.dieSize; i++) { for (let i = tNum; i <= rollConf.dieSize; i++) {
@ -244,10 +245,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.exploding.nums.push(i); rollConf.exploding.nums.push(i);
} }
break; break;
case "!o<": case '!o<':
rollConf.exploding.once = true; rollConf.exploding.once = true;
// falls through as !o< functions the same as !< in this context // falls through as !o< functions the same as !< in this context
case "!<": case '!<':
// Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why) // Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why)
rollConf.exploding.on = true; rollConf.exploding.on = true;
for (let i = 1; i <= tNum; i++) { for (let i = 1; i <= tNum; i++) {
@ -266,36 +267,36 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
// Verify the parse, throwing errors for every invalid config // Verify the parse, throwing errors for every invalid config
if (rollConf.dieCount < 0) { if (rollConf.dieCount < 0) {
throw new Error("NoZerosAllowed_base"); throw new Error('NoZerosAllowed_base');
} }
if (rollConf.dieCount === 0 || rollConf.dieSize === 0) { if (rollConf.dieCount === 0 || rollConf.dieSize === 0) {
throw new Error("NoZerosAllowed_base"); throw new Error('NoZerosAllowed_base');
} }
// Since only one drop or keep option can be active, count how many are active to throw the right error // Since only one drop or keep option can be active, count how many are active to throw the right error
let dkdkCnt = 0; let dkdkCnt = 0;
[rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach(e => { [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach((e) => {
log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`); log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`);
if (e) { if (e) {
dkdkCnt++; dkdkCnt++;
} }
}); });
if (dkdkCnt > 1) { if (dkdkCnt > 1) {
throw new Error("FormattingError_dk"); throw new Error('FormattingError_dk');
} }
if (rollConf.drop.on && rollConf.drop.count === 0) { if (rollConf.drop.on && rollConf.drop.count === 0) {
throw new Error("NoZerosAllowed_drop"); throw new Error('NoZerosAllowed_drop');
} }
if (rollConf.keep.on && rollConf.keep.count === 0) { if (rollConf.keep.on && rollConf.keep.count === 0) {
throw new Error("NoZerosAllowed_keep"); throw new Error('NoZerosAllowed_keep');
} }
if (rollConf.dropHigh.on && rollConf.dropHigh.count === 0) { if (rollConf.dropHigh.on && rollConf.dropHigh.count === 0) {
throw new Error("NoZerosAllowed_dropHigh"); throw new Error('NoZerosAllowed_dropHigh');
} }
if (rollConf.keepLow.on && rollConf.keepLow.count === 0) { if (rollConf.keepLow.on && rollConf.keepLow.count === 0) {
throw new Error("NoZerosAllowed_keepLow"); throw new Error('NoZerosAllowed_keepLow');
} }
if (rollConf.reroll.on && rollConf.reroll.nums.indexOf(0) >= 0) { if (rollConf.reroll.on && rollConf.reroll.nums.indexOf(0) >= 0) {
throw new Error("NoZerosAllowed_reroll"); throw new Error('NoZerosAllowed_reroll');
} }
// Roll the roll // Roll the roll
@ -331,7 +332,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rerolled: false, rerolled: false,
exploding: false, exploding: false,
critHit: false, critHit: false,
critFail: false critFail: false,
}; };
// Begin counting the number of loops to prevent from getting into an infinite loop // Begin counting the number of loops to prevent from getting into an infinite loop
@ -342,7 +343,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`); log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
// If loopCount gets too high, stop trying to calculate infinity // If loopCount gets too high, stop trying to calculate infinity
if (loopCount > MAXLOOPS) { if (loopCount > MAXLOOPS) {
throw new Error("MaxLoopsExceeded"); throw new Error('MaxLoopsExceeded');
} }
// Copy the template to fill out for this iteration // Copy the template to fill out for this iteration
@ -356,13 +357,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
if (rollConf.critScore.on && rollConf.critScore.range.indexOf(rolling.roll) >= 0) { if (rollConf.critScore.on && rollConf.critScore.range.indexOf(rolling.roll) >= 0) {
rolling.critHit = true; rolling.critHit = true;
} else if (!rollConf.critScore.on) { } else if (!rollConf.critScore.on) {
rolling.critHit = (rolling.roll === rollConf.dieSize); rolling.critHit = rolling.roll === rollConf.dieSize;
} }
// If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
if (rollConf.critFail.on && rollConf.critFail.range.indexOf(rolling.roll) >= 0) { if (rollConf.critFail.on && rollConf.critFail.range.indexOf(rolling.roll) >= 0) {
rolling.critFail = true; rolling.critFail = true;
} else if (!rollConf.critFail.on) { } else if (!rollConf.critFail.on) {
rolling.critFail = (rolling.roll === 1); rolling.critFail = rolling.roll === 1;
} }
// Push the newly created roll and loop again // Push the newly created roll and loop again
@ -376,7 +377,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`); log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
// If loopCount gets too high, stop trying to calculate infinity // If loopCount gets too high, stop trying to calculate infinity
if (loopCount > MAXLOOPS) { if (loopCount > MAXLOOPS) {
throw new Error("MaxLoopsExceeded"); throw new Error('MaxLoopsExceeded');
} }
// If we need to reroll this roll, flag its been replaced and... // If we need to reroll this roll, flag its been replaced and...
@ -393,21 +394,24 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) { if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) {
newRoll.critHit = true; newRoll.critHit = true;
} else if (!rollConf.critScore.on) { } else if (!rollConf.critScore.on) {
newRoll.critHit = (newRoll.roll === rollConf.dieSize); newRoll.critHit = newRoll.roll === rollConf.dieSize;
} }
// If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) { if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) {
newRoll.critFail = true; newRoll.critFail = true;
} else if (!rollConf.critFail.on) { } else if (!rollConf.critFail.on) {
newRoll.critFail = (newRoll.roll === 1); newRoll.critFail = newRoll.roll === 1;
} }
// Slot this new roll in after the current iteration so it can be processed in the next loop // Slot this new roll in after the current iteration so it can be processed in the next loop
rollSet.splice(i + 1, 0, newRoll); rollSet.splice(i + 1, 0, newRoll);
} else if (rollConf.exploding.on && !rollSet[i].rerolled && (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) && (!rollConf.exploding.once || !rollSet[i].exploding)) { } else if (
rollConf.exploding.on && !rollSet[i].rerolled && (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) &&
(!rollConf.exploding.once || !rollSet[i].exploding)
) {
// If we have exploding.nums set, use those to determine the exploding range, and make sure if !o is on, make sure we don't repeatedly explode // If we have exploding.nums set, use those to determine the exploding range, and make sure if !o is on, make sure we don't repeatedly explode
// If it exploded, we keep both, so no flags need to be set // If it exploded, we keep both, so no flags need to be set
// Copy the template to fill out for this iteration // Copy the template to fill out for this iteration
const newRoll = JSON.parse(JSON.stringify(templateRoll)); const newRoll = JSON.parse(JSON.stringify(templateRoll));
// If maximiseRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll // If maximiseRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll
@ -419,13 +423,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) { if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) {
newRoll.critHit = true; newRoll.critHit = true;
} else if (!rollConf.critScore.on) { } else if (!rollConf.critScore.on) {
newRoll.critHit = (newRoll.roll === rollConf.dieSize); newRoll.critHit = newRoll.roll === rollConf.dieSize;
} }
// If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) { if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) {
newRoll.critFail = true; newRoll.critFail = true;
} else if (!rollConf.critFail.on) { } else if (!rollConf.critFail.on) {
newRoll.critFail = (newRoll.roll === 1); newRoll.critFail = newRoll.roll === 1;
} }
// Slot this new roll in after the current iteration so it can be processed in the next loop // Slot this new roll in after the current iteration so it can be processed in the next loop
@ -444,7 +448,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
for (let i = 0; i < rollSet.length; i++) { for (let i = 0; i < rollSet.length; i++) {
// If loopCount gets too high, stop trying to calculate infinity // If loopCount gets too high, stop trying to calculate infinity
if (loopCount > MAXLOOPS) { if (loopCount > MAXLOOPS) {
throw new Error("MaxLoopsExceeded"); throw new Error('MaxLoopsExceeded');
} }
log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[i])}`); log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[i])}`);
@ -477,9 +481,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
if (dropCount < 0) { if (dropCount < 0) {
dropCount = 0; dropCount = 0;
} }
} } // For inverted drop and keep, order must be flipped to greatest to least before the simple subtraction can determine how many to drop
// For inverted drop and keep, order must be flipped to greatest to least before the simple subtraction can determine how many to drop
// Protections are in to prevent the dropCount from going below 0 or more than the valid rolls to drop // Protections are in to prevent the dropCount from going below 0 or more than the valid rolls to drop
else if (rollConf.dropHigh.on) { else if (rollConf.dropHigh.on) {
rollSet.reverse(); rollSet.reverse();
@ -500,7 +502,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
while (dropCount > 0 && i < rollSet.length) { while (dropCount > 0 && i < rollSet.length) {
// If loopCount gets too high, stop trying to calculate infinity // If loopCount gets too high, stop trying to calculate infinity
if (loopCount > MAXLOOPS) { if (loopCount > MAXLOOPS) {
throw new Error("MaxLoopsExceeded"); throw new Error('MaxLoopsExceeded');
} }
log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`); log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);

View File

@ -2,49 +2,49 @@
// RollSet is used to preserve all information about a calculated roll // RollSet is used to preserve all information about a calculated roll
export type RollSet = { export type RollSet = {
origidx: number, origidx: number;
roll: number, roll: number;
dropped: boolean, dropped: boolean;
rerolled: boolean, rerolled: boolean;
exploding: boolean, exploding: boolean;
critHit: boolean, critHit: boolean;
critFail: boolean critFail: boolean;
}; };
// SolvedStep is used to preserve information while math is being performed on the roll // SolvedStep is used to preserve information while math is being performed on the roll
export type SolvedStep = { export type SolvedStep = {
total: number, total: number;
details: string, details: string;
containsCrit: boolean, containsCrit: boolean;
containsFail: boolean containsFail: boolean;
}; };
// ReturnData is the temporary internal type used before getting turned into SolvedRoll // ReturnData is the temporary internal type used before getting turned into SolvedRoll
export type ReturnData = { export type ReturnData = {
rollTotal: number, rollTotal: number;
rollPostFormat: string, rollPostFormat: string;
rollDetails: string, rollDetails: string;
containsCrit: boolean, containsCrit: boolean;
containsFail: boolean, containsFail: boolean;
initConfig: string initConfig: string;
}; };
// SolvedRoll is the complete solved and formatted roll, or the error said roll created // SolvedRoll is the complete solved and formatted roll, or the error said roll created
export type SolvedRoll = { export type SolvedRoll = {
error: boolean, error: boolean;
errorMsg: string, errorMsg: string;
errorCode: string, errorCode: string;
line1: string, line1: string;
line2: string, line2: string;
line3: string line3: string;
}; };
// CountDetails is the object holding the count data for creating the Count Embed // CountDetails is the object holding the count data for creating the Count Embed
export type CountDetails = { export type CountDetails = {
total: number, total: number;
successful: number, successful: number;
failed: number, failed: number;
rerolled: number, rerolled: number;
dropped: number, dropped: number;
exploded: number exploded: number;
}; };

View File

@ -5,22 +5,23 @@
*/ */
import { import {
log,
// Log4Deno deps // Log4Deno deps
LT, log LT,
} from "../../deps.ts"; } from '../../deps.ts';
import { SolvedStep } from "./solver.d.ts"; import { SolvedStep } from './solver.d.ts';
// fullSolver(conf, wrapDetails) returns one condensed SolvedStep // fullSolver(conf, wrapDetails) returns one condensed SolvedStep
// fullSolver is a function that recursively solves the full roll and math // fullSolver is a function that recursively solves the full roll and math
export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean): SolvedStep => { export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean): SolvedStep => {
// Initialize PEMDAS // Initialize PEMDAS
const signs = ["^", "*", "/", "%", "+", "-"]; const signs = ['^', '*', '/', '%', '+', '-'];
const stepSolve = { const stepSolve = {
total: 0, total: 0,
details: "", details: '',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}; };
// If entering with a single number, note it now // If entering with a single number, note it now
@ -30,10 +31,10 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
} }
// Evaluate all parenthesis // Evaluate all parenthesis
while (conf.indexOf("(") > -1) { while (conf.indexOf('(') > -1) {
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`); log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
// Get first open parenthesis // Get first open parenthesis
const openParen = conf.indexOf("("); const openParen = conf.indexOf('(');
let closeParen = -1; let closeParen = -1;
let nextParen = 0; let nextParen = 0;
@ -41,12 +42,12 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
for (let i = openParen; i < conf.length; i++) { for (let i = openParen; i < conf.length; i++) {
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`); log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`);
// If we hit an open, add one (this includes the openParen we start with), if we hit a close, subtract one // If we hit an open, add one (this includes the openParen we start with), if we hit a close, subtract one
if (conf[i] === "(") { if (conf[i] === '(') {
nextParen++; nextParen++;
} else if (conf[i] === ")") { } else if (conf[i] === ')') {
nextParen--; nextParen--;
} }
// When nextParen reaches 0 again, we will have found the matching closing parenthesis and can safely exit the for loop // When nextParen reaches 0 again, we will have found the matching closing parenthesis and can safely exit the for loop
if (nextParen === 0) { if (nextParen === 0) {
closeParen = i; closeParen = i;
@ -56,11 +57,11 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
// Make sure we did find the correct closing paren, if not, error out now // Make sure we did find the correct closing paren, if not, error out now
if (closeParen === -1 || closeParen < openParen) { if (closeParen === -1 || closeParen < openParen) {
throw new Error("UnbalancedParens"); throw new Error('UnbalancedParens');
} }
// Replace the itemes between openParen and closeParen (including the parens) with its solved equilvalent by calling the solver on the items between openParen and closeParen (excluding the parens) // Replace the itemes between openParen and closeParen (including the parens) with its solved equilvalent by calling the solver on the items between openParen and closeParen (excluding the parens)
conf.splice(openParen, (closeParen + 1), fullSolver(conf.slice((openParen + 1), closeParen), true)); conf.splice(openParen, closeParen + 1, fullSolver(conf.slice(openParen + 1, closeParen), true));
// Determing if we need to add in a multiplication sign to handle implicit multiplication (like "(4)2" = 8) // Determing if we need to add in a multiplication sign to handle implicit multiplication (like "(4)2" = 8)
// insertedMult flags if there was a multiplication sign inserted before the parens // insertedMult flags if there was a multiplication sign inserted before the parens
@ -68,20 +69,20 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
// Check if a number was directly before openParen and slip in the "*" if needed // Check if a number was directly before openParen and slip in the "*" if needed
if (((openParen - 1) > -1) && (signs.indexOf(conf[openParen - 1].toString()) === -1)) { if (((openParen - 1) > -1) && (signs.indexOf(conf[openParen - 1].toString()) === -1)) {
insertedMult = true; insertedMult = true;
conf.splice(openParen, 0, "*"); conf.splice(openParen, 0, '*');
} }
// Check if a number is directly after closeParen and slip in the "*" if needed // Check if a number is directly after closeParen and slip in the "*" if needed
if (!insertedMult && (((openParen + 1) < conf.length) && (signs.indexOf(conf[openParen + 1].toString()) === -1))) { if (!insertedMult && (((openParen + 1) < conf.length) && (signs.indexOf(conf[openParen + 1].toString()) === -1))) {
conf.splice((openParen + 1), 0, "*"); conf.splice(openParen + 1, 0, '*');
} else if (insertedMult && (((openParen + 2) < conf.length) && (signs.indexOf(conf[openParen + 2].toString()) === -1))) { } else if (insertedMult && (((openParen + 2) < conf.length) && (signs.indexOf(conf[openParen + 2].toString()) === -1))) {
// insertedMult is utilized here to let us account for an additional item being inserted into the array (the "*" from before openParn) // insertedMult is utilized here to let us account for an additional item being inserted into the array (the "*" from before openParn)
conf.splice((openParen + 2), 0, "*"); conf.splice(openParen + 2, 0, '*');
} }
} }
// Evaluate all EMMDAS by looping thru each teir of operators (exponential is the higehest teir, addition/subtraction the lowest) // Evaluate all EMMDAS by looping thru each teir of operators (exponential is the higehest teir, addition/subtraction the lowest)
const allCurOps = [["^"], ["*", "/", "%"], ["+", "-"]]; const allCurOps = [['^'], ['*', '/', '%'], ['+', '-']];
allCurOps.forEach(curOps => { allCurOps.forEach((curOps) => {
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`); log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
// Iterate thru all operators/operands in the conf // Iterate thru all operators/operands in the conf
for (let i = 0; i < conf.length; i++) { for (let i = 0; i < conf.length; i++) {
@ -96,15 +97,15 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
let oper2 = NaN; let oper2 = NaN;
const subStepSolve = { const subStepSolve = {
total: NaN, total: NaN,
details: "", details: '',
containsCrit: false, containsCrit: false,
containsFail: false containsFail: false,
}; };
// Flag to prevent infinte loop when dealing with negative numbers (such as [[-1+20]]) // Flag to prevent infinte loop when dealing with negative numbers (such as [[-1+20]])
let shouldDecrement = true; let shouldDecrement = true;
// If operand1 is a SolvedStep, populate our subStepSolve with its details and crit/fail flags // If operand1 is a SolvedStep, populate our subStepSolve with its details and crit/fail flags
if (typeof operand1 === "object") { if (typeof operand1 === 'object') {
oper1 = operand1.total; oper1 = operand1.total;
subStepSolve.details = `${operand1.details}\\${conf[i]}`; subStepSolve.details = `${operand1.details}\\${conf[i]}`;
subStepSolve.containsCrit = operand1.containsCrit; subStepSolve.containsCrit = operand1.containsCrit;
@ -122,7 +123,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
} }
// If operand2 is a SolvedStep, populate our subStepSolve with its details without overriding what operand1 filled in // If operand2 is a SolvedStep, populate our subStepSolve with its details without overriding what operand1 filled in
if (typeof operand2 === "object") { if (typeof operand2 === 'object') {
oper2 = operand2.total; oper2 = operand2.total;
subStepSolve.details += operand2.details; subStepSolve.details += operand2.details;
subStepSolve.containsCrit = subStepSolve.containsCrit || operand2.containsCrit; subStepSolve.containsCrit = subStepSolve.containsCrit || operand2.containsCrit;
@ -135,42 +136,42 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
// Make sure neither operand is NaN before continuing // Make sure neither operand is NaN before continuing
if (isNaN(oper1) || isNaN(oper2)) { if (isNaN(oper1) || isNaN(oper2)) {
throw new Error("OperandNaN"); throw new Error('OperandNaN');
} }
// Verify a second time that both are numbers before doing math, throwing an error if necessary // Verify a second time that both are numbers before doing math, throwing an error if necessary
if ((typeof oper1 === "number") && (typeof oper2 === "number")) { if ((typeof oper1 === 'number') && (typeof oper2 === 'number')) {
// Finally do the operator on the operands, throw an error if the operator is not found // Finally do the operator on the operands, throw an error if the operator is not found
switch (conf[i]) { switch (conf[i]) {
case "^": case '^':
subStepSolve.total = Math.pow(oper1, oper2); subStepSolve.total = Math.pow(oper1, oper2);
break; break;
case "*": case '*':
subStepSolve.total = oper1 * oper2; subStepSolve.total = oper1 * oper2;
break; break;
case "/": case '/':
subStepSolve.total = oper1 / oper2; subStepSolve.total = oper1 / oper2;
break; break;
case "%": case '%':
subStepSolve.total = oper1 % oper2; subStepSolve.total = oper1 % oper2;
break; break;
case "+": case '+':
subStepSolve.total = oper1 + oper2; subStepSolve.total = oper1 + oper2;
break; break;
case "-": case '-':
subStepSolve.total = oper1 - oper2; subStepSolve.total = oper1 - oper2;
break; break;
default: default:
throw new Error("OperatorWhat"); throw new Error('OperatorWhat');
} }
} else { } else {
throw new Error("EMDASNotNumber"); throw new Error('EMDASNotNumber');
} }
// Determine if we actually did math or just smashed a - sign onto a number // Determine if we actually did math or just smashed a - sign onto a number
if (shouldDecrement) { if (shouldDecrement) {
// Replace the two operands and their operator with our subStepSolve // Replace the two operands and their operator with our subStepSolve
conf.splice((i - 1), 3, subStepSolve); conf.splice(i - 1, 3, subStepSolve);
// Because we are messing around with the array we are iterating thru, we need to back up one idx to make sure every operator gets processed // Because we are messing around with the array we are iterating thru, we need to back up one idx to make sure every operator gets processed
i--; i--;
} else { } else {
@ -184,17 +185,17 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
// If we somehow have more than one item left in conf at this point, something broke, throw an error // If we somehow have more than one item left in conf at this point, something broke, throw an error
if (conf.length > 1) { if (conf.length > 1) {
log(LT.LOG, `ConfWHAT? ${JSON.stringify(conf)}`); log(LT.LOG, `ConfWHAT? ${JSON.stringify(conf)}`);
throw new Error("ConfWhat"); throw new Error('ConfWhat');
} else if (singleNum && (typeof (conf[0]) === "number")) { } else if (singleNum && (typeof (conf[0]) === 'number')) {
// If we are only left with a number, populate the stepSolve with it // If we are only left with a number, populate the stepSolve with it
stepSolve.total = conf[0]; stepSolve.total = conf[0];
stepSolve.details = conf[0].toString(); stepSolve.details = conf[0].toString();
} else { } else {
// Else fully populate the stepSolve with what was computed // Else fully populate the stepSolve with what was computed
stepSolve.total = (<SolvedStep>conf[0]).total; stepSolve.total = (<SolvedStep> conf[0]).total;
stepSolve.details = (<SolvedStep>conf[0]).details; stepSolve.details = (<SolvedStep> conf[0]).details;
stepSolve.containsCrit = (<SolvedStep>conf[0]).containsCrit; stepSolve.containsCrit = (<SolvedStep> conf[0]).containsCrit;
stepSolve.containsFail = (<SolvedStep>conf[0]).containsFail; stepSolve.containsFail = (<SolvedStep> conf[0]).containsFail;
} }
// If this was a nested call, add on parens around the details to show what math we've done // If this was a nested call, add on parens around the details to show what math we've done
@ -204,7 +205,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
// If our total has reached undefined for some reason, error out now // If our total has reached undefined for some reason, error out now
if (stepSolve.total === undefined) { if (stepSolve.total === undefined) {
throw new Error("UndefinedStep"); throw new Error('UndefinedStep');
} }
return stepSolve; return stepSolve;

View File

@ -6,8 +6,8 @@
import { import {
// Discordeno deps // Discordeno deps
sendMessage sendMessage,
} from "../deps.ts"; } from '../deps.ts';
// ask(prompt) returns string // ask(prompt) returns string
// ask prompts the user at command line for message // ask prompts the user at command line for message
@ -18,7 +18,7 @@ const ask = async (question: string, stdin = Deno.stdin, stdout = Deno.stdout):
await stdout.write(new TextEncoder().encode(question)); await stdout.write(new TextEncoder().encode(question));
// Read console's input into answer // Read console's input into answer
const n = <number>await stdin.read(buf); const n = <number> await stdin.read(buf);
const answer = new TextDecoder().decode(buf.subarray(0, n)); const answer = new TextDecoder().decode(buf.subarray(0, n));
return answer.trim(); return answer.trim();
@ -31,64 +31,55 @@ const cmdPrompt = async (logChannel: bigint, botName: string): Promise<void> =>
while (!done) { while (!done) {
// Get a command and its args // Get a command and its args
const fullCmd = await ask("cmd> "); const fullCmd = await ask('cmd> ');
// Split the args off of the command and prep the command // Split the args off of the command and prep the command
const args = fullCmd.split(" "); const args = fullCmd.split(' ');
const command = args.shift()?.toLowerCase(); const command = args.shift()?.toLowerCase();
// All commands below here // All commands below here
// exit or e // exit or e
// Fully closes the bot // Fully closes the bot
if (command === "exit" || command === "e") { if (command === 'exit' || command === 'e') {
console.log(`${botName} Shutting down.\n\nGoodbye.`); console.log(`${botName} Shutting down.\n\nGoodbye.`);
done = true; done = true;
Deno.exit(0); Deno.exit(0);
} } // stop
// stop
// Closes the CLI only, leaving the bot running truly headless // Closes the CLI only, leaving the bot running truly headless
else if (command === "stop") { else if (command === 'stop') {
console.log(`Closing ${botName} CLI. Bot will continue to run.\n\nGoodbye.`); console.log(`Closing ${botName} CLI. Bot will continue to run.\n\nGoodbye.`);
done = true; done = true;
} } // m [channel] [message]
// m [channel] [message]
// Sends [message] to specified [channel] // Sends [message] to specified [channel]
else if (command === "m") { else if (command === 'm') {
try { try {
const channelId = args.shift() || ""; const channelId = args.shift() || '';
const message = args.join(" "); const message = args.join(' ');
sendMessage(BigInt(channelId), message).catch(reason => { sendMessage(BigInt(channelId), message).catch((reason) => {
console.error(reason); console.error(reason);
}); });
} } catch (e) {
catch (e) {
console.error(e); console.error(e);
} }
} } // ml [message]
// ml [message]
// Sends a message to the specified log channel // Sends a message to the specified log channel
else if (command === "ml") { else if (command === 'ml') {
const message = args.join(" "); const message = args.join(' ');
sendMessage(logChannel, message).catch(reason => { sendMessage(logChannel, message).catch((reason) => {
console.error(reason); console.error(reason);
}); });
} } // help or h
// help or h
// Shows a basic help menu // Shows a basic help menu
else if (command === "help" || command === "h") { else if (command === 'help' || command === 'h') {
console.log(`${botName} CLI Help:\n\nAvailable Commands:\n exit - closes bot\n stop - closes the CLI\n m [ChannelID] [messgae] - sends message to specific ChannelID as the bot\n ml [message] sends a message to the specified botlog\n help - this message`); console.log(
} `${botName} CLI Help:\n\nAvailable Commands:\n exit - closes bot\n stop - closes the CLI\n m [ChannelID] [messgae] - sends message to specific ChannelID as the bot\n ml [message] sends a message to the specified botlog\n help - this message`,
);
// Unhandled commands die here } // Unhandled commands die here
else { else {
console.log("undefined command"); console.log('undefined command');
} }
} }
}; };