convert indentation to tabs

This commit is contained in:
Oliver-Akins 2021-12-02 13:36:13 -06:00
parent f8910e4f30
commit 7ef7e3c3dc
22 changed files with 1985 additions and 765 deletions

1
.gitignore vendored
View file

@ -1,6 +1,5 @@
dist/ dist/
data/*.json data/*.json
src/commands
config.json config.json

1425
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@
"express": "^4.17.1", "express": "^4.17.1",
"express-tl": "^1.0.2", "express-tl": "^1.0.2",
"fs": "0.0.1-security", "fs": "0.0.1-security",
"mathjs": "^6.2.1", "mathjs": "^10.0.0",
"request": "^2.88.0", "request": "^2.88.0",
"request-promise-native": "^1.0.7", "request-promise-native": "^1.0.7",
"tmi.js": "^1.5.0", "tmi.js": "^1.5.0",

View file

@ -22,175 +22,175 @@ var service_last_rans: any = {}
export const REGISTER_COMMAND = (metadata: cmd_metadata): boolean => { export const REGISTER_COMMAND = (metadata: cmd_metadata): boolean => {
// Ensure command gets added correctly // Ensure command gets added correctly
try { try {
commands.push(new Command(metadata)); commands.push(new Command(metadata));
SORT_COMMANDS(commands); SORT_COMMANDS(commands);
return true; return true;
} catch (error) { } catch (error) {
return false; return false;
}; };
}; };
export const HANDLE_MESSAGE = (ctx: msg_data): string => { export const HANDLE_MESSAGE = (ctx: msg_data): string => {
let config: config = LOAD_CONFIG(); let config: config = LOAD_CONFIG();
let datetime = new Date(); let datetime = new Date();
let timezone = datetime.toLocaleTimeString("en-us", {timeZoneName:"short"}).split(" ")[2]; let timezone = datetime.toLocaleTimeString("en-us", {timeZoneName:"short"}).split(" ")[2];
let date = `${datetime.getFullYear()}-${datetime.getMonth()+1}-${datetime.getDate()}` let date = `${datetime.getFullYear()}-${datetime.getMonth()+1}-${datetime.getDate()}`
+ ` @ ${datetime.getHours()}:${datetime.getMinutes()} ${timezone}`; + ` @ ${datetime.getHours()}:${datetime.getMinutes()} ${timezone}`;
// Confirmation handling: // Confirmation handling:
// Check if we need any confirmations from users // Check if we need any confirmations from users
for (var index in confirms) { for (var index in confirms) {
let confirmation = confirms[index]; let confirmation = confirms[index];
let response: CONFIRM_TYPE = confirmation.matches( let response: CONFIRM_TYPE = confirmation.matches(
ctx.user, ctx.channel, ctx.message ctx.user, ctx.channel, ctx.message
); );
if (!["no_match", "expired"].includes(response)) { if (!["no_match", "expired"].includes(response)) {
confirms.splice(parseInt(index), 1); confirms.splice(parseInt(index), 1);
let cmd_resp = confirmation.run(response); let cmd_resp = confirmation.run(response);
// Check if we have a response to log // Check if we have a response to log
if (cmd_resp) { if (cmd_resp) {
log({ log({
title: `Log Entry (Confirmation Response):`, title: `Log Entry (Confirmation Response):`,
msg: `Command:\`\`\`\n${ctx.message}\n\`\`\`\n\nResponse:\`\`\`\n${cmd_resp}\n\`\`\``, msg: `Command:\`\`\`\n${ctx.message}\n\`\`\`\n\nResponse:\`\`\`\n${cmd_resp}\n\`\`\``,
embed: true, embed: true,
fields: { fields: {
"Date:": date, "Date:": date,
"Is Mod:": ctx.level >= PERM.MOD, "Is Mod:": ctx.level >= PERM.MOD,
"Is Admin:": ctx.level >= PERM.ADMIN, "Is Admin:": ctx.level >= PERM.ADMIN,
"Level:": ctx.level, "Level:": ctx.level,
"Channel:": ctx.channel, "Channel:": ctx.channel,
"Username": ctx.user, "Username": ctx.user,
"Platform": ctx.source, "Platform": ctx.source,
"Confirm Type": response "Confirm Type": response
}, },
no_stdout: true no_stdout: true
}); });
}; };
return cmd_resp; return cmd_resp;
} }
else if (response === "expired") { else if (response === "expired") {
confirms.splice(parseInt(index), 1); confirms.splice(parseInt(index), 1);
confirmation.run(response); confirmation.run(response);
}; };
}; };
// SECTION: Global command cooldowns // SECTION: Global command cooldowns
if (config.bot.COOLDOWN_TYPE === "GLOBAL" && ctx.cooldown) { if (config.bot.COOLDOWN_TYPE === "GLOBAL" && ctx.cooldown) {
if (global_last_ran) { if (global_last_ran) {
if (Date.now() - global_last_ran < config.bot.COOLDOWN_TIME * 1000) { if (Date.now() - global_last_ran < config.bot.COOLDOWN_TIME * 1000) {
return null; return null;
}; };
}; };
global_last_ran = Date.now(); global_last_ran = Date.now();
} }
// !SECTION: Global command cooldowns // !SECTION: Global command cooldowns
// SECTION: Service command cooldowns // SECTION: Service command cooldowns
else if (config.bot.COOLDOWN_TYPE === "SERVICE" && ctx.cooldown) { else if (config.bot.COOLDOWN_TYPE === "SERVICE" && ctx.cooldown) {
if (service_last_rans[ctx.source]) { if (service_last_rans[ctx.source]) {
if (Date.now() - service_last_rans[ctx.source] < config.bot.COOLDOWN_TIME * 1000) { if (Date.now() - service_last_rans[ctx.source] < config.bot.COOLDOWN_TIME * 1000) {
return null; return null;
}; };
}; };
service_last_rans[ctx.source] = Date.now(); service_last_rans[ctx.source] = Date.now();
}; };
// !SECTION: Service command cooldowns // !SECTION: Service command cooldowns
// SECTION: Flag parsing // SECTION: Flag parsing
ctx.flags = GET_FLAGS(ctx.message); ctx.flags = GET_FLAGS(ctx.message);
// removing arguments that are indicated as flags // removing arguments that are indicated as flags
let re = new RegExp(`\\${FLAG_INDICATOR}\\w+\\s?`, `g`); let re = new RegExp(`\\${FLAG_INDICATOR}\\w+\\s?`, `g`);
ctx.message = ctx.message.replace(re, ``); ctx.message = ctx.message.replace(re, ``);
// !SECTION: Flag parsing // !SECTION: Flag parsing
// Check all registered commands // Check all registered commands
for (var cmd of commands) { for (var cmd of commands) {
// NOTE: Checking if message doesn't match // NOTE: Checking if message doesn't match
if (!cmd.matches(ctx.message.toLowerCase())) { continue; }; if (!cmd.matches(ctx.message.toLowerCase())) { continue; };
// NOTE: Permission checking // NOTE: Permission checking
if (ctx.level < cmd.level) { if (ctx.level < cmd.level) {
if (config.bot.INVALID_PERM_ERROR) { if (config.bot.INVALID_PERM_ERROR) {
return `Invalid Permissions, you must be at least level ${cmd.level}, you are level ${ctx.level}.`; return `Invalid Permissions, you must be at least level ${cmd.level}, you are level ${ctx.level}.`;
}; };
return null; return null;
}; };
// NOTE: per-command cooldown // NOTE: per-command cooldown
if (config.bot.COOLDOWN_TYPE === "COMMAND" && ctx.cooldown) { if (config.bot.COOLDOWN_TYPE === "COMMAND" && ctx.cooldown) {
if (cmd.last_ran) { if (cmd.last_ran) {
if (Date.now() - cmd.last_ran < config.bot.COOLDOWN_TIME * 1000) { if (Date.now() - cmd.last_ran < config.bot.COOLDOWN_TIME * 1000) {
return null; return null;
}; };
}; };
cmd.last_ran = Date.now(); cmd.last_ran = Date.now();
}; };
// NOTE: Case sensitivity // NOTE: Case sensitivity
if (!cmd.case_sensitive) { if (!cmd.case_sensitive) {
ctx.message = ctx.message.toLowerCase(); ctx.message = ctx.message.toLowerCase();
}; };
// NOTE: Argument parsing // NOTE: Argument parsing
let args = ctx.message let args = ctx.message
.slice(config.bot.PREFIX.length) .slice(config.bot.PREFIX.length)
.split(" ") .split(" ")
.slice(cmd.full_name.split(" ").length); .slice(cmd.full_name.split(" ").length);
// Ensure the use supplied enough arguments // Ensure the use supplied enough arguments
if (args.length < cmd.mand_args) { if (args.length < cmd.mand_args) {
return `Not enough arguments, missing argument: \`${cmd.arg_list[args.length]}\``; return `Not enough arguments, missing argument: \`${cmd.arg_list[args.length]}\``;
}; };
let response = cmd.execute(ctx, args); let response = cmd.execute(ctx, args);
log({ log({
title: `Log Entry:`, title: `Log Entry:`,
msg: `Command:\`\`\`\n${ctx.message}\n\`\`\`\n\nResponse:\`\`\`\n${response}\n\`\`\``, msg: `Command:\`\`\`\n${ctx.message}\n\`\`\`\n\nResponse:\`\`\`\n${response}\n\`\`\``,
embed: true, embed: true,
fields: { fields: {
"Date:": date, "Date:": date,
"Is Mod:": ctx.level >= PERM.MOD, "Is Mod:": ctx.level >= PERM.MOD,
"Is Admin:": ctx.level >= PERM.ADMIN, "Is Admin:": ctx.level >= PERM.ADMIN,
"Level:": ctx.level, "Level:": ctx.level,
"Channel:": ctx.channel, "Channel:": ctx.channel,
"Username": ctx.user, "Username": ctx.user,
"Platform": ctx.source "Platform": ctx.source
}, },
no_stdout: true no_stdout: true
}); });
return response; return response;
}; };
return null; return null;
}; };

View file

@ -18,15 +18,15 @@ export const CONFIRM_TIMEOUT: number = 5;
export const PERM: perms = { export const PERM: perms = {
ALL: 0, ALL: 0,
MOD: 1, MOD: 1,
ADMIN: 2 ADMIN: 2
}; };
export const LIMIT = { export const LIMIT = {
DISCORD: 2000, DISCORD: 2000,
TWITCH: 500 TWITCH: 500
}; };

View file

@ -17,21 +17,21 @@ let args = process.argv.slice(2);
// Not enough arguments // Not enough arguments
if (args.length < 1) { if (args.length < 1) {
console.error(`Too few arguments.`); console.error(`Too few arguments.`);
process.exit(1); process.exit(1);
} }
if (args.includes("--test")) { if (args.includes("--test")) {
process.exit(run_tests(args.includes("--silent"))); process.exit(run_tests(args.includes("--silent")));
}; };
if (args.includes("--twitch")) { if (args.includes("--twitch")) {
run_twitch(); run_twitch();
}; };
if (args.includes("--discord")) { if (args.includes("--discord")) {
run_discord(); run_discord();
}; };

View file

@ -15,115 +15,115 @@ const Eris = require("eris");
export const run_discord = (): void => { export const run_discord = (): void => {
const config: config = LOAD_CONFIG(); const config: config = LOAD_CONFIG();
let bot = new Eris(config.DEV ? config.discord.DEV_TOKEN : config.discord.OAUTH_TOKEN); let bot = new Eris(config.DEV ? config.discord.DEV_TOKEN : config.discord.OAUTH_TOKEN);
bot.on("ready", () => { bot.on("ready", () => {
log({ msg: `* Connected to Discord gateway` }); log({ msg: `* Connected to Discord gateway` });
}); });
// Message handler // Message handler
bot.on("messageCreate", (msg: any) => { bot.on("messageCreate", (msg: any) => {
try { try {
// Ensure message to parse // Ensure message to parse
if (msg.content.length === 0) { return; }; if (msg.content.length === 0) { return; };
// SECTION: Exit conditions // SECTION: Exit conditions
// NOTE: Ensure not a system message // NOTE: Ensure not a system message
if (msg.type !== 0) { return; } if (msg.type !== 0) { return; }
// NOTE: Ensure channel type is GUILD_TEXT // NOTE: Ensure channel type is GUILD_TEXT
else if (msg.channel.type !== 0) { return; } else if (msg.channel.type !== 0) { return; }
// NOTE: Ensure not a webhook or Clyde // NOTE: Ensure not a webhook or Clyde
else if (msg.author.discriminator === "0000") { return; } else if (msg.author.discriminator === "0000") { return; }
// NOTE: Ensure not a bot // NOTE: Ensure not a bot
else if (msg.member.bot) { return; } else if (msg.member.bot) { return; }
// !SECTION: Exit conditions // !SECTION: Exit conditions
var is_mod = msg.member.roles.filter( var is_mod = msg.member.roles.filter(
(x: string) => { return config.discord.MOD_ROLES.includes(x); } (x: string) => { return config.discord.MOD_ROLES.includes(x); }
).length > 0; ).length > 0;
var is_admin = config.discord.ADMIN.includes(msg.member.id); var is_admin = config.discord.ADMIN.includes(msg.member.id);
var level = PERM.ALL; var level = PERM.ALL;
if (is_mod) { level = PERM.MOD; }; if (is_mod) { level = PERM.MOD; };
if (is_admin) { level = PERM.ADMIN; }; if (is_admin) { level = PERM.ADMIN; };
var response: string | void = HANDLE_MESSAGE({ var response: string | void = HANDLE_MESSAGE({
channel: `Discord:${msg.member.guild.id}`, channel: `Discord:${msg.member.guild.id}`,
level: level, level: level,
message: msg.content.trim().replace(/\n/g, ""), message: msg.content.trim().replace(/\n/g, ""),
source: "Discord", source: "Discord",
user: msg.author.username, user: msg.author.username,
cooldown: true, cooldown: true,
test: false test: false
}); });
// NOTE: Ensure response isn't null // NOTE: Ensure response isn't null
if (response !== null) { if (response !== null) {
// NOTE: Reply with string // NOTE: Reply with string
bot.createMessage(msg.channel.id, response) bot.createMessage(msg.channel.id, response)
}; };
} }
// SECTION: Error Handling // SECTION: Error Handling
catch (error) { catch (error) {
log_error({ log_error({
"embeds": [ "embeds": [
{ {
"title": `${error.name}`, "title": `${error.name}`,
"color": 13238272, "color": 13238272,
"description": `**Error Message:**\n\`\`\`\n${error.message}\n\`\`\``, "description": `**Error Message:**\n\`\`\`\n${error.message}\n\`\`\``,
"fields": [ "fields": [
{ {
"name": "**Message Context:**", "name": "**Message Context:**",
"value": `\`\`\`\n${JSON.stringify(msg, null, 2)}\n\`\`\`` "value": `\`\`\`\n${JSON.stringify(msg, null, 2)}\n\`\`\``
}, },
{ {
"name": "**Message Content:**", "name": "**Message Content:**",
"value": `\`\`\`json\n${msg.content}\`\`\`` "value": `\`\`\`json\n${msg.content}\`\`\``
}, },
{ {
"name": "**Is Mod:**", "name": "**Is Mod:**",
"value": `\`${is_mod}\``, "value": `\`${is_mod}\``,
"inline": true "inline": true
}, },
{ {
"name": "**Is Admin:**", "name": "**Is Admin:**",
"value": `\`${is_admin}\``, "value": `\`${is_admin}\``,
"inline": true "inline": true
}, },
{ {
"name": "**Channel:**", "name": "**Channel:**",
"value": `\`${msg.channel.name}\``, "value": `\`${msg.channel.name}\``,
"inline": true "inline": true
}, },
{ {
"name": "Source", "name": "Source",
"value": "Discord", "value": "Discord",
"inline": true "inline": true
} }
] ]
} }
] ]
}); });
}; };
// !SECTION: Error Handling // !SECTION: Error Handling
}); });
bot.connect(); bot.connect();
}; };

View file

@ -17,56 +17,56 @@ var fail_count = 0;
export const run_tests = (silent: boolean): number => { export const run_tests = (silent: boolean): number => {
// Run through each test // Run through each test
for (var test of tests) { for (var test of tests) {
let response = HANDLE_MESSAGE({ let response = HANDLE_MESSAGE({
message: test.msg_meta.message, message: test.msg_meta.message,
level: test.msg_meta.level, level: test.msg_meta.level,
user: TEST_USER, user: TEST_USER,
cooldown: false, cooldown: false,
source: test.msg_meta.source, source: test.msg_meta.source,
channel: test.msg_meta.channel || TEST_CHANNEL, channel: test.msg_meta.channel || TEST_CHANNEL,
test: true test: true
}); });
// If the confirmation message is defined, trigger a confirmation // If the confirmation message is defined, trigger a confirmation
if (test.confirm_msg) { if (test.confirm_msg) {
response = HANDLE_MESSAGE({ response = HANDLE_MESSAGE({
message: test.confirm_msg.message, message: test.confirm_msg.message,
level: test.confirm_msg.level, level: test.confirm_msg.level,
user: TEST_USER, user: TEST_USER,
cooldown: false, cooldown: false,
source: test.confirm_msg.source, source: test.confirm_msg.source,
channel: test.msg_meta.channel || TEST_CHANNEL, channel: test.msg_meta.channel || TEST_CHANNEL,
test: true test: true
}); });
}; };
// Compare outputs // Compare outputs
if (test.expected_return != response) { if (test.expected_return != response) {
fail_count++; fail_count++;
if (!silent) { if (!silent) {
console.log("====================================================="); console.log("=====================================================");
console.log(`Test ${test.id} failed`); console.log(`Test ${test.id} failed`);
console.log(` Expected: "${test.expected_return}"`); console.log(` Expected: "${test.expected_return}"`);
console.log(` Received: "${response}"`); console.log(` Received: "${response}"`);
}; };
}; };
}; };
// Check if we are being silent // Check if we are being silent
if (!silent && fail_count > 0) { if (!silent && fail_count > 0) {
console.log("====================================================="); console.log("=====================================================");
}; };
// Output summary // Output summary
console.log(`Tests: ${fail_count} tests failed out of ${tests.length} tests.`); console.log(`Tests: ${fail_count} tests failed out of ${tests.length} tests.`);
console.log(` ${Math.round(((tests.length - fail_count) / tests.length) * 100)}% passed`); console.log(` ${Math.round(((tests.length - fail_count) / tests.length) * 100)}% passed`);
return fail_count; return fail_count;
}; };

View file

@ -19,130 +19,130 @@ import * as tmi from "tmi.js";
export const run_twitch = (): void => { export const run_twitch = (): void => {
const config: config = LOAD_CONFIG(); const config: config = LOAD_CONFIG();
// Init Client // Init Client
const client = tmi.Client({ const client = tmi.Client({
options: { options: {
debug: config.DEV, debug: config.DEV,
clientId: config.twitch.CLIENT_ID clientId: config.twitch.CLIENT_ID
}, },
connection: { connection: {
secure: true, secure: true,
reconnect: true reconnect: true
}, },
identity: { identity: {
username: config.twitch.USERNAME, username: config.twitch.USERNAME,
password: config.twitch.OAUTH_TOKEN password: config.twitch.OAUTH_TOKEN
}, },
channels: config.twitch.CHANNELS channels: config.twitch.CHANNELS
}); });
// SECTION: Handle messages // SECTION: Handle messages
client.on("message", (channel: string, context: tmi.Userstate, message: string, self: boolean) => { client.on("message", (channel: string, context: tmi.Userstate, message: string, self: boolean) => {
try { try {
// SECTION: Context checking // SECTION: Context checking
// NOTE: Ensure not self // NOTE: Ensure not self
if (self) { return; } if (self) { return; }
// NOTE: Ensure text channel, not whisper or anything else weird. // NOTE: Ensure text channel, not whisper or anything else weird.
else if (context["message-type"] !== "chat") { return; } else if (context["message-type"] !== "chat") { return; }
// !SECTION: Context checking // !SECTION: Context checking
// SECTION: Context parsing // SECTION: Context parsing
var is_admin = config.twitch.ADMIN.includes(context.username); var is_admin = config.twitch.ADMIN.includes(context.username);
var is_mod = ( var is_mod = (
context.mod || context.mod ||
context.badges ? context.badges.moderator === "1" : false || context.badges ? context.badges.moderator === "1" : false ||
context.badges ? context.badges.broadcaster === "1" : false || context.badges ? context.badges.broadcaster === "1" : false ||
is_admin is_admin
); );
var level = PERM.ALL; var level = PERM.ALL;
if (is_mod) { level = PERM.MOD; }; if (is_mod) { level = PERM.MOD; };
if (is_admin) { level = PERM.ADMIN; }; if (is_admin) { level = PERM.ADMIN; };
// !SECTION: Context parsing // !SECTION: Context parsing
// NOTE: Get response // NOTE: Get response
let response: string = HANDLE_MESSAGE({ let response: string = HANDLE_MESSAGE({
message: message, message: message,
channel: `Twitch:${channel}`, channel: `Twitch:${channel}`,
level: level, level: level,
source: "Twitch", source: "Twitch",
user: context.username, user: context.username,
cooldown: true, cooldown: true,
test: false test: false
}); });
// NOTE: Ensure response isn't null // NOTE: Ensure response isn't null
if (response) { if (response) {
client.say( client.say(
channel, channel,
response.replace(/`/g, `"`) response.replace(/`/g, `"`)
); );
}; };
} catch (error) { } catch (error) {
log_error({ log_error({
"embeds": [ "embeds": [
{ {
"title": `${error.name}`, "title": `${error.name}`,
"color": 13238272, "color": 13238272,
"description": `**Error Message:**\n\`\`\`\n${error.message}\n\`\`\``, "description": `**Error Message:**\n\`\`\`\n${error.message}\n\`\`\``,
"fields": [ "fields": [
{ {
"name": "**Message Context:**", "name": "**Message Context:**",
"value": `\`\`\`\n${JSON.stringify(context, null, 2)}\n\`\`\`` "value": `\`\`\`\n${JSON.stringify(context, null, 2)}\n\`\`\``
}, },
{ {
"name": "**Message Content:**", "name": "**Message Content:**",
"value": `\`\`\`json\n${message}\`\`\`` "value": `\`\`\`json\n${message}\`\`\``
}, },
{ {
"name": "**Is Mod:**", "name": "**Is Mod:**",
"value": `\`${is_mod}\``, "value": `\`${is_mod}\``,
"inline": true "inline": true
}, },
{ {
"name": "**Is Admin:**", "name": "**Is Admin:**",
"value": `\`${is_admin}\``, "value": `\`${is_admin}\``,
"inline": true "inline": true
}, },
{ {
"name": "**Channel:**", "name": "**Channel:**",
"value": `\`${channel}\``, "value": `\`${channel}\``,
"inline": true "inline": true
}, },
{ {
"name": "Source", "name": "Source",
"value": "Twitch", "value": "Twitch",
"inline": true "inline": true
} }
] ]
} }
] ]
}); });
} }
}); });
// !SECTION // !SECTION
client.on("disconnected", (reason: string) => { client.on("disconnected", (reason: string) => {
log({ msg: `* Disconnected from Twitch w/ reason: ${reason}` }); log({ msg: `* Disconnected from Twitch w/ reason: ${reason}` });
}); });
client.on("connected", (addr: string, port: number) => { client.on("connected", (addr: string, port: number) => {
log({ msg: `* Connected to Twitch on \`${addr}:${port}\`` }); log({ msg: `* Connected to Twitch on \`${addr}:${port}\`` });
}); });
client.connect().catch((_) => {}) client.connect().catch((_) => {})
}; };

View file

@ -10,33 +10,33 @@ type CONFIRM_TYPE = "confirm"|"deny"|"no_match"|"expired"|"invalid";
interface positions { interface positions {
head?: string; head?: string;
head_2?: string; head_2?: string;
table_head?: string; table_head?: string;
table_foot?: string; table_foot?: string;
usage?: string; usage?: string;
} }
interface alert_structure { interface alert_structure {
info?: positions; info?: positions;
warn?: positions; warn?: positions;
error?: positions; error?: positions;
} }
interface cmd_metadata { interface cmd_metadata {
executable(context: msg_data, args: string[]): string; executable(context: msg_data, args: string[]): string;
flags: {[key: string]: string}; flags: {[key: string]: string};
requires_confirm: boolean; requires_confirm: boolean;
alerts?: alert_structure; alerts?: alert_structure;
case_sensitive: boolean; case_sensitive: boolean;
description: string; description: string;
keywords?: string[][]; keywords?: string[][];
arg_info: string[]; arg_info: string[];
opt_args: number; opt_args: number;
group?: string; group?: string;
args: string[]; args: string[];
level: number; level: number;
name?: string; name?: string;
} }

56
src/types/config.d.ts vendored
View file

@ -5,53 +5,53 @@
// //
interface auth_options { interface auth_options {
OAUTH_TOKEN: string; OAUTH_TOKEN: string;
SECRET?: string; SECRET?: string;
CLIENT_ID?: string; CLIENT_ID?: string;
ADMIN: string[]; ADMIN: string[];
} }
interface discord_options extends auth_options { interface discord_options extends auth_options {
PERMISSIONS_VALUE: string; PERMISSIONS_VALUE: string;
MOD_ROLES: string; MOD_ROLES: string;
DEV_TOKEN: string; DEV_TOKEN: string;
} }
interface twitch_options extends auth_options { interface twitch_options extends auth_options {
CHANNELS: [string]; CHANNELS: [string];
USERNAME: string; USERNAME: string;
} }
interface bot_options { interface bot_options {
PREFIX: string; PREFIX: string;
COOLDOWN_TIME: number; COOLDOWN_TIME: number;
INVALID_PERM_ERROR: boolean; INVALID_PERM_ERROR: boolean;
COOLDOWN_TYPE: "GLOBAL"|"COMMAND"|"SERVICE"; COOLDOWN_TYPE: "GLOBAL"|"COMMAND"|"SERVICE";
} }
interface web_server_options { interface web_server_options {
ADDRESS: string; ADDRESS: string;
ROOT: string; ROOT: string;
PORT: number; PORT: number;
} }
interface config { interface config {
DEV: boolean; DEV: boolean;
twitch: twitch_options; twitch: twitch_options;
discord: discord_options; discord: discord_options;
bot: bot_options; bot: bot_options;
webhooks: { webhooks: {
ENABLED: boolean; ENABLED: boolean;
LOGGING: string; LOGGING: string;
ERROR?: string; ERROR?: string;
TWITCH_MISSED_BITS?: string; TWITCH_MISSED_BITS?: string;
}; };
web: web_server_options; web: web_server_options;
extra: { [index: string]: any } extra: { [index: string]: any }
} }

View file

@ -9,12 +9,12 @@ type platform = "Discord"|"Twitch"
interface msg_data { interface msg_data {
cooldown: boolean; cooldown: boolean;
source: platform; source: platform;
channel: string; channel: string;
message: string; message: string;
flags?: string[]; flags?: string[];
level: number; level: number;
test: boolean; test: boolean;
user: string; user: string;
} }

16
src/types/option.d.ts vendored
View file

@ -6,12 +6,12 @@
interface option { interface option {
aliases: string[]; aliases: string[];
name: string; name: string;
points: { points: {
[key: string]: number [key: string]: number
}; };
total: number; total: number;
data_version: string; data_version: string;
hidden: boolean; hidden: boolean;
} }

View file

@ -5,7 +5,7 @@
// //
interface perms { interface perms {
ALL: number; ALL: number;
MOD: number; MOD: number;
ADMIN: number; ADMIN: number;
} }

View file

@ -6,19 +6,19 @@
interface test_msg_data { interface test_msg_data {
source: platform; source: platform;
channel?: string; channel?: string;
message: string; message: string;
level: number; level: number;
} }
interface test { interface test {
datafile_should_exist: "EXISTS"|"NOT_EXISTS"|"IGNORES"; datafile_should_exist: "EXISTS"|"NOT_EXISTS"|"IGNORES";
datafile_populated?: boolean; datafile_populated?: boolean;
confirm_msg?: test_msg_data; confirm_msg?: test_msg_data;
expected_return: string; expected_return: string;
msg_meta: test_msg_data; msg_meta: test_msg_data;
links: {[key: string]: string} links: {[key: string]: string}
id: string; id: string;
} }

View file

@ -6,9 +6,9 @@
interface log_data { interface log_data {
msg: string; msg: string;
title?: string; title?: string;
embed?: boolean; embed?: boolean;
fields?: object; fields?: object;
no_stdout?: boolean; no_stdout?: boolean;
} }

View file

@ -10,96 +10,96 @@ import { LOAD_CONFIG } from "./Config";
export class Command { export class Command {
readonly requires_confirm: boolean; readonly requires_confirm: boolean;
readonly case_sensitive: boolean; readonly case_sensitive: boolean;
readonly keywords: string[][]; readonly keywords: string[][];
readonly arg_list: string[]; readonly arg_list: string[];
readonly arg_info: string[]; readonly arg_info: string[];
readonly mand_args: number; readonly mand_args: number;
readonly full_name: string; readonly full_name: string;
readonly opt_args: number; readonly opt_args: number;
readonly syntax: string; readonly syntax: string;
readonly level: number; readonly level: number;
readonly group: string; readonly group: string;
readonly flags: object; readonly flags: object;
readonly info: string; readonly info: string;
readonly name: string; readonly name: string;
readonly alert: alert_structure; readonly alert: alert_structure;
private _func: (context: msg_data, args: string[]) => string; private _func: (context: msg_data, args: string[]) => string;
public last_ran: number; public last_ran: number;
constructor (metadata: cmd_metadata) { constructor (metadata: cmd_metadata) {
this.mand_args = metadata.args.length - metadata.opt_args; this.mand_args = metadata.args.length - metadata.opt_args;
this.case_sensitive = metadata.case_sensitive; this.case_sensitive = metadata.case_sensitive;
this.opt_args = metadata.opt_args; this.opt_args = metadata.opt_args;
this.info = metadata.description; this.info = metadata.description;
this._func = metadata.executable; this._func = metadata.executable;
this.arg_list = metadata.args; this.arg_list = metadata.args;
this.group = metadata.group; this.group = metadata.group;
this.level = metadata.level; this.level = metadata.level;
this.name = metadata.name; this.name = metadata.name;
this.keywords = metadata.keywords || []; this.keywords = metadata.keywords || [];
this.alert = metadata.alerts; this.alert = metadata.alerts;
this.requires_confirm = metadata.requires_confirm; this.requires_confirm = metadata.requires_confirm;
this.full_name = this.group ? `${this.group} ${this.name}` : `${this.name}`; this.full_name = this.group ? `${this.group} ${this.name}` : `${this.name}`;
this.arg_info = metadata.arg_info; this.arg_info = metadata.arg_info;
this.flags = metadata.flags; this.flags = metadata.flags;
// NOTE: Create syntax dynamically // NOTE: Create syntax dynamically
let config: config = LOAD_CONFIG(); let config: config = LOAD_CONFIG();
this.syntax = config.bot.PREFIX; this.syntax = config.bot.PREFIX;
this.syntax += this.group ? `${this.group} ${this.name}` : `${this.name}`; this.syntax += this.group ? `${this.group} ${this.name}` : `${this.name}`;
if (this.arg_list.length > 0) { if (this.arg_list.length > 0) {
this.syntax += ` ${this.arg_list.join(" ")}`; this.syntax += ` ${this.arg_list.join(" ")}`;
} }
}; };
// Does the user's message match a command // Does the user's message match a command
public matches (message: string): boolean { public matches (message: string): boolean {
const config: config = LOAD_CONFIG(); const config: config = LOAD_CONFIG();
// Only check for name/group match if the name is specified // Only check for name/group match if the name is specified
if (this.name != null) { if (this.name != null) {
// Construct the regex // Construct the regex
let regex: string = `^${config.bot.PREFIX}`; let regex: string = `^${config.bot.PREFIX}`;
if (this.group != null) { regex += `(${this.group}\ )`; }; if (this.group != null) { regex += `(${this.group}\ )`; };
regex += `${this.name}`; regex += `${this.name}`;
if (message.match(new RegExp(regex)) != null) { if (message.match(new RegExp(regex)) != null) {
return true; return true;
}; };
}; };
// Compare the keyword sets to the message // Compare the keyword sets to the message
for (var keyword_set of this.keywords) { for (var keyword_set of this.keywords) {
let all_included: boolean = true; let all_included: boolean = true;
// Check that each keyword is in the message // Check that each keyword is in the message
for (var keyword of keyword_set) { for (var keyword of keyword_set) {
if (!message.includes(keyword)) { if (!message.includes(keyword)) {
all_included = false; all_included = false;
break; break;
}; };
}; };
if (all_included) { return true; }; if (all_included) { return true; };
} }
return false; return false;
}; };
public execute (ctx: msg_data, args: string[]): string { public execute (ctx: msg_data, args: string[]): string {
return this._func(ctx, args) return this._func(ctx, args)
}; };
}; };
@ -107,53 +107,53 @@ export class Command {
export class Confirmation { export class Confirmation {
readonly username: string; readonly username: string;
readonly channel: string; readonly channel: string;
private data: any; private data: any;
private created: number; private created: number;
private timeout: number; private timeout: number;
private callback: (type: CONFIRM_TYPE, data?: any) => string; private callback: (type: CONFIRM_TYPE, data?: any) => string;
constructor ( constructor (
username: string, username: string,
channel: string, channel: string,
timeout: number, timeout: number,
cb: (type: CONFIRM_TYPE, data?: any) => string, cb: (type: CONFIRM_TYPE, data?: any) => string,
data?: any data?: any
) { ) {
this.username = username; this.username = username;
this.channel = channel; this.channel = channel;
this.created = Date.now(); this.created = Date.now();
this.callback = cb; this.callback = cb;
this.timeout = timeout * 1000; this.timeout = timeout * 1000;
this.data = data; this.data = data;
}; };
public matches (user: string, channel: string, msg: string): CONFIRM_TYPE { public matches (user: string, channel: string, msg: string): CONFIRM_TYPE {
const config: config = LOAD_CONFIG(); const config: config = LOAD_CONFIG();
// Timeout checking // Timeout checking
if (Date.now() - this.created > this.timeout) { return "expired"; } if (Date.now() - this.created > this.timeout) { return "expired"; }
// basic user checking // basic user checking
else if (this.username !== user) { return "no_match"; } else if (this.username !== user) { return "no_match"; }
else if (this.channel !== channel) { return "no_match"; } else if (this.channel !== channel) { return "no_match"; }
// Positive or negative match? // Positive or negative match?
else if (msg.match(`^${config.bot.PREFIX}[Yy](es)?$`)) { return "confirm"; } else if (msg.match(`^${config.bot.PREFIX}[Yy](es)?$`)) { return "confirm"; }
else if (msg.match(`^${config.bot.PREFIX}[Nn](o)?$`)) { return "deny"; } else if (msg.match(`^${config.bot.PREFIX}[Nn](o)?$`)) { return "deny"; }
// Not valid // Not valid
else { return "invalid"; }; else { return "invalid"; };
}; };
public run (type: CONFIRM_TYPE): string { public run (type: CONFIRM_TYPE): string {
return this.callback(type, this.data) return this.callback(type, this.data)
} }
}; };

View file

@ -10,22 +10,22 @@ import { readFileSync, writeFile } from "fs"
export const LOAD_CONFIG = (): config => { export const LOAD_CONFIG = (): config => {
let config = require.resolve("../../config.json"); let config = require.resolve("../../config.json");
let data = readFileSync(config); let data = readFileSync(config);
// @ts-ignore // @ts-ignore
return JSON.parse(data); return JSON.parse(data);
}; };
export const UPDATE_CONFIG = (data: config): void => { export const UPDATE_CONFIG = (data: config): void => {
writeFile( writeFile(
require.resolve("../../config.json"), require.resolve("../../config.json"),
JSON.stringify(data, null, 2), JSON.stringify(data, null, 2),
() => { () => {
console.log("* [Config] Config written to."); console.log("* [Config] Config written to.");
} }
) )
}; };

View file

@ -11,28 +11,28 @@ import { FLAG_INDICATOR } from "../constants";
export const GET_FLAGS = (msg: string): string[] => { export const GET_FLAGS = (msg: string): string[] => {
// Array of flags included in the message // Array of flags included in the message
let flags: string[] = [] let flags: string[] = []
// Check each parameter of the message // Check each parameter of the message
for (var argument of msg.split(" ")) { for (var argument of msg.split(" ")) {
// Check if the argument is indicated as a flag // Check if the argument is indicated as a flag
if (argument.startsWith(FLAG_INDICATOR)) { if (argument.startsWith(FLAG_INDICATOR)) {
// Check each flag // Check each flag
for (var temp_flag of argument.slice(FLAG_INDICATOR.length)) { for (var temp_flag of argument.slice(FLAG_INDICATOR.length)) {
temp_flag = temp_flag.toLowerCase(); temp_flag = temp_flag.toLowerCase();
// Only add flags to the array once // Only add flags to the array once
if (!flags.includes(temp_flag)) { if (!flags.includes(temp_flag)) {
flags.push(temp_flag); flags.push(temp_flag);
}; };
}; };
}; };
}; };
return flags; return flags;
} }

View file

@ -9,31 +9,31 @@ import { Command } from "./Command";
export const SORT_OPTIONS = (data: option[]): option[] => { export const SORT_OPTIONS = (data: option[]): option[] => {
return data.sort((a: option, b: option): number => { return data.sort((a: option, b: option): number => {
let a_total: number = a.total; let a_total: number = a.total;
let b_total: number = b.total; let b_total: number = b.total;
if (a_total < b_total) { return 1; } if (a_total < b_total) { return 1; }
else if (a_total > b_total) { return -1; } else if (a_total > b_total) { return -1; }
else { return 0; }; else { return 0; };
}); });
}; };
export const SORT_COMMANDS = (data: Command[]): Command[] => { export const SORT_COMMANDS = (data: Command[]): Command[] => {
return data.sort((a: Command, b: Command): number => { return data.sort((a: Command, b: Command): number => {
let a_name: string = a.full_name; let a_name: string = a.full_name;
let b_name: string = b.full_name; let b_name: string = b.full_name;
if (a_name < b_name) { return -1; } if (a_name < b_name) { return -1; }
else if (a_name > b_name) { return 1; } else if (a_name > b_name) { return 1; }
else { return 0; }; else { return 0; };
}); });
}; };

View file

@ -16,16 +16,16 @@ export const PREFIX: string = config.bot.PREFIX;
export let tests: test[] = [ export let tests: test[] = [
{ {
id: `general:01`, id: `general:01`,
links: {}, links: {},
datafile_should_exist: `IGNORES`, datafile_should_exist: `IGNORES`,
msg_meta: { msg_meta: {
source: `Twitch`, source: `Twitch`,
message: `potato salad`, message: `potato salad`,
level: PERM.ALL, level: PERM.ALL,
channel: TEST_CHANNEL channel: TEST_CHANNEL
}, },
expected_return: null expected_return: null
} }
]; ];

View file

@ -16,65 +16,65 @@ const config: config = LOAD_CONFIG();
export const log = (context: log_data) => { export const log = (context: log_data) => {
// Should we output the data to the console, ensure the data is console-outputable // Should we output the data to the console, ensure the data is console-outputable
if (config.DEV && !context.no_stdout) { if (config.DEV && !context.no_stdout) {
console.log(context.msg); console.log(context.msg);
}; };
// Are we embedding the response or not? // Are we embedding the response or not?
if (context.embed) { if (context.embed) {
let payload = { let payload = {
"content": "Log Entry:", "content": "Log Entry:",
"embeds": [ "embeds": [
{ {
color: 43520, color: 43520,
title: context.title, title: context.title,
description: context.msg, description: context.msg,
fields: [] fields: []
} }
] ]
}; };
// Add fields // Add fields
for (var field in context.fields) { for (var field in context.fields) {
payload.embeds[0].fields.push({ payload.embeds[0].fields.push({
name: field, name: field,
value: context.fields[field], value: context.fields[field],
inline: true inline: true
}); });
}; };
push(payload, "LOGGING") push(payload, "LOGGING")
} else { } else {
push({ push({
"content": context.msg "content": context.msg
}, "LOGGING"); }, "LOGGING");
}; };
}; };
export const log_error = (payload: any) => { export const log_error = (payload: any) => {
push(payload, "ERROR"); push(payload, "ERROR");
}; };
export const push = (payload: any, webhook: WEBHOOK_TYPE) => { export const push = (payload: any, webhook: WEBHOOK_TYPE) => {
// Output to stdout? // Output to stdout?
if (payload.content && !payload.no_stdout && payload.no_stdout != undefined) { if (payload.content && !payload.no_stdout && payload.no_stdout != undefined) {
console.log(payload.content) console.log(payload.content)
}; };
// Don't try to execute webhook if they aren't enabled // Don't try to execute webhook if they aren't enabled
if (!config.webhooks.ENABLED) { return; } if (!config.webhooks.ENABLED) { return; }
requests.post({ requests.post({
uri: config.webhooks[webhook], uri: config.webhooks[webhook],
body: payload, body: payload,
json: true json: true
}).catch((_: any) => { }).catch((_: any) => {
console.error("OHNO, Shit Went DOWWWNNNNNN"); console.error("OHNO, Shit Went DOWWWNNNNNN");
}); });
}; };