From c15e3ab86e859f7b71fe14807c010f6c5576face Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 09:38:48 +0530 Subject: [PATCH 01/10] Matched the case of `Utilities` caused import error --- src/handler/sern.ts | 440 ++++++++++++++++++++++---------------------- 1 file changed, 217 insertions(+), 223 deletions(-) diff --git a/src/handler/sern.ts b/src/handler/sern.ts index ebf0a46..0fa6c0a 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -1,23 +1,12 @@ -import * as Files from './utilities/readFile'; -import type * as Utils from './utilities/preprocessors/args'; +import * as Files from './Utilities/readFile'; +import type * as Utils from './Utilities/Preprocessors/args'; -import type { - Arg, - Context, - Visibility, - possibleOutput -} from '../types/handler'; +import type { Arg, Context, Visibility, possibleOutput } from '../types/handler'; -import type { - ApplicationCommandOptionData, - Awaitable, - Client, - CommandInteraction, - Message -} from 'discord.js'; +import type { ApplicationCommandOptionData, Awaitable, Client, CommandInteraction, Message } from 'discord.js'; import { Ok, Result, None, Some } from 'ts-results'; -import { isBot, hasPrefix, fmt } from './utilities/messageHelpers'; +import { isBot, hasPrefix, fmt } from './Utilities/messageHelpers'; import Logger from './logger'; /** @@ -25,221 +14,226 @@ import Logger from './logger'; */ export class Handler { - private wrapper: Wrapper; + private wrapper: Wrapper; - /** - * @constructor - * @param {Wrapper} wrapper The data that is required to run sern handler - */ - - constructor( - wrapper: Wrapper, - ) { - this.wrapper = wrapper; - this.client - - /** - * On ready, builds command data and registers them all - * from command directory - **/ - - .on('ready', async () => { - Files.buildData(this) - .then(data => this.registerModules(data)) - if (wrapper.init !== undefined) wrapper.init(this); - new Logger().tableRam() - }) + /** + * @constructor + * @param {Wrapper} wrapper The data that is required to run sern handler + */ - .on('messageCreate', async (message: Message) => { - if (isBot(message) || !hasPrefix(message, this.prefix)) return; - if (message.channel.type === 'DM') return; // TODO: Handle dms + constructor(wrapper: Wrapper) { + this.wrapper = wrapper; + this.client - const tryFmt = fmt(message, this.prefix) - const commandName = tryFmt.shift()!; - const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName) - if (module === undefined) { - message.channel.send('Unknown legacy command') - return; - } - const cmdResult = (await this.commandResult(module, message, tryFmt.join(' '))) - if (cmdResult === undefined) return; + /** + * On ready, builds command data and registers them all + * from command directory + **/ - message.channel.send(cmdResult); + .on('ready', async () => { + Files.buildData(this).then((data) => this.registerModules(data)); + if (wrapper.init !== undefined) wrapper.init(this); + new Logger().tableRam(); + }) - }) + .on('messageCreate', async (message: Message) => { + if (isBot(message) || !hasPrefix(message, this.prefix)) return; + if (message.channel.type === 'DM') return; // TODO: Handle dms - .on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; - const module = Files.Commands.get(interaction.commandName); - const res = await this.interactionResult(module, interaction); - if (res === undefined) return; - await interaction.reply(res); - }); + const tryFmt = fmt(message, this.prefix); + const commandName = tryFmt.shift()!; + const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName); + if (module === undefined) { + message.channel.send('Unknown legacy command'); + return; + } + const cmdResult = await this.commandResult(module, message, tryFmt.join(' ')); + if (cmdResult === undefined) return; + + message.channel.send(cmdResult); + }) + + .on('interactionCreate', async (interaction) => { + if (!interaction.isCommand()) return; + const module = Files.Commands.get(interaction.commandName); + const res = await this.interactionResult(module, interaction); + if (res === undefined) return; + await interaction.reply(res); + }); + } + + /** + * + * @param {Files.CommandVal | undefined} module Command file information + * @param {CommandInteraction} interaction The Discord.js command interaction (DiscordJS#CommandInteraction)) + * @returns {possibleOutput | undefined} Takes return value and replies it, if possible input + */ + + private async interactionResult( + module: Files.CommandVal | undefined, + interaction: CommandInteraction, + ): Promise { + if (module === undefined) return 'Unknown slash command!'; + const name = Array.from(Files.Commands.keys()).find((it) => it === interaction.commandName); + if (name === undefined) return `Could not find ${interaction.commandName} command!`; + + if (module.mod.type < CommandType.SLASH) return 'This is not a slash command'; + + const context = { message: None, interaction: Some(interaction) }; + const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok(''); + + if (parsedArgs.err) return parsedArgs.val; + + return (await module.mod.delegate(context, parsedArgs))?.val; + } + + /** + * + * @param {Files.CommandVal | undefined} module Command file information + * @param {Message} message The message object + * @param {string} args Anything after the command + * @returns Takes return value and replies it, if possible input + */ + + private async commandResult( + module: Files.CommandVal | undefined, + message: Message, + args: string, + ): Promise { + if (module?.mod === undefined) return 'Unknown legacy command'; + if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command`; + if (module.mod.visibility === 'private') { + const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test; + if (checkIsTestServer === undefined) + return 'This command has the private modifier but is not registered under Handler#privateServers'; + if (checkIsTestServer !== module.mod.test) { + const msg = `This command is only available on test servers.`; // TODO: Customizable private message + + return msg; + } } + const context = { + message: Some(message), + interaction: None, + }; + const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok(''); + if (parsedArgs.err) return parsedArgs.val; + return (await module.mod.delegate(context, parsedArgs))?.val; + } - /** - * - * @param {Files.CommandVal | undefined} module Command file information - * @param {CommandInteraction} interaction The Discord.js command interaction (DiscordJS#CommandInteraction)) - * @returns {possibleOutput | undefined} Takes return value and replies it, if possible input - */ - - private async interactionResult( - module: Files.CommandVal | undefined, - interaction: CommandInteraction): Promise { + /** + * This function chains `Files.buildData` + * @param {{name: string, mod: Module, absPath: string}} modArr module information + */ - if (module === undefined) return 'Unknown slash command!'; - const name = Array.from(Files.Commands.keys()).find(it => it === interaction.commandName); - if (name === undefined) return `Could not find ${interaction.commandName} command!`; - - if (module.mod.type < CommandType.SLASH) return 'This is not a slash command'; - - const context = { message: None, interaction: Some(interaction) } - const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok(''); - - if (parsedArgs.err) return parsedArgs.val; - - return (await module.mod.delegate(context, parsedArgs))?.val; - } - - /** - * - * @param {Files.CommandVal | undefined} module Command file information - * @param {Message} message The message object - * @param {string} args Anything after the command - * @returns Takes return value and replies it, if possible input - */ - - private async commandResult(module: Files.CommandVal | undefined, message: Message, args: string): Promise { - if (module?.mod === undefined) return 'Unknown legacy command'; - if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command` - if (module.mod.visibility === 'private') { - const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test; - if (checkIsTestServer === undefined) return 'This command has the private modifier but is not registered under Handler#privateServers'; - if (checkIsTestServer !== module.mod.test) { - const msg = `This command is only available on test servers.`; // TODO: Customizable private message - - return msg; + private async registerModules( + modArr: { + name: string; + mod: Module; + absPath: string; + }[], + ) { + for await (const { name, mod, absPath } of modArr) { + const cmdName = Files.fmtFileName(name); + switch (mod.type) { + case 1: + Files.Commands.set(cmdName, { mod, options: [] }); + break; + case 2: + case 1 | 2: + { + const options = (await import(absPath)).options as ApplicationCommandOptionData[]; + Files.Commands.set(cmdName, { mod, options: options ?? [] }); + switch (mod.visibility) { + case 'private': { + // Reloading guild slash commands + await this.reloadSlash(cmdName, mod.desc, options); + } + case 'public': { + // Creating global commands + await this.client.application!.commands.create({ + name: cmdName, + description: mod.desc, + options, + }); + } } + } + break; + default: + throw Error(`SernHandlerError: ${name} with ${mod.visibility} is not a valid module type.`); + } + + if (mod.alias.length > 0) { + for (const alias of mod.alias) { + Files.Alias.set(alias, { mod, options: [] }); } - const context = { - message: Some(message), - interaction: None - } - const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok(''); - if (parsedArgs.err) return parsedArgs.val; - return (await module.mod.delegate(context, parsedArgs))?.val; + } } - - /** - * This function chains `Files.buildData` - * @param {{name: string, mod: Module, absPath: string}} modArr module information - */ + } - private async registerModules( - modArr: { - name: string, - mod: Module, - absPath: string - }[] - ) { - for await (const { name, mod, absPath } of modArr) { - const cmdName = Files.fmtFileName(name); - switch (mod.type) { - case 1: Files.Commands.set(cmdName, { mod, options: [] }); break; - case 2: - case (1 | 2): { - const options = ((await import(absPath)).options as ApplicationCommandOptionData[]) - Files.Commands.set(cmdName, { mod, options: options ?? [] }); - switch (mod.visibility) { - case 'private': { - // Reloading guild slash commands - await this.reloadSlash(cmdName, mod.desc, options) - } - case 'public': { - // Creating global commands - await this.client.application!.commands - .create({ - name: cmdName, - description: mod.desc, - options - }); - } - } - } break; - default: throw Error(`SernHandlerError: ${name} with ${mod.visibility} is not a valid module type.`); - } + /** + * + * @param {string} cmdName name of command + * @param {string} description description of command + * @param {ApplicationCommandOptionData[]} options any options for the slash command + */ - if (mod.alias.length > 0) { - for (const alias of mod.alias) { - Files.Alias.set(alias, { mod, options: [] }) - } - } - } - } + private async reloadSlash( + cmdName: string, + description: string, + options: ApplicationCommandOptionData[], + ): Promise { + for (const { id } of this.privateServers) { + const guild = await this.client.guilds.fetch(id); - /** - * - * @param {string} cmdName name of command - * @param {string} description description of command - * @param {ApplicationCommandOptionData[]} options any options for the slash command - */ - - private async reloadSlash( - cmdName: string, - description: string, - options: ApplicationCommandOptionData[] - ) : Promise { - for (const { id } of this.privateServers) { - const guild = (await this.client.guilds.fetch(id)); + guild.commands.create({ + name: cmdName, + description, + options, + }); + } + } - guild.commands.create({ - name: cmdName, - description, - options - }); - } - } + /** + * @readonly + * @returns {string} The prefix used for legacy commands + */ - /** - * @readonly - * @returns {string} The prefix used for legacy commands - */ - - get prefix(): string { - return this.wrapper.prefix; - } - - /** - * @readonly - * @returns {string} Directory of the commands folder - */ - - get commandDir(): string { - return this.wrapper.commands; - } + get prefix(): string { + return this.wrapper.prefix; + } - /** - * @readonly - * @returns {Client} the discord.js client (DiscordJS#Client)); - */ - - get client(): Client { - return this.wrapper.client; - } - - /** - * @readonly - * @returns {{test: boolean, id: string}[]} Private server ID for testing or personal use - */ - - get privateServers(): { test: boolean, id: string }[] { - return this.wrapper.privateServers; - } + /** + * @readonly + * @returns {string} Directory of the commands folder + */ + + get commandDir(): string { + return this.wrapper.commands; + } + + /** + * @readonly + * @returns {Client} the discord.js client (DiscordJS#Client)); + */ + + get client(): Client { + return this.wrapper.client; + } + + /** + * @readonly + * @returns {{test: boolean, id: string}[]} Private server ID for testing or personal use + */ + + get privateServers(): { test: boolean; id: string }[] { + return this.wrapper.privateServers; + } } /** - * An object to be passed into Sern.Handler constructor. + * An object to be passed into Sern.Handler constructor. * @typedef {object} Wrapper * @property {readonly Client} client * @property {readonly string} prefix @@ -248,15 +242,15 @@ export class Handler { * @property {readonly {test: boolean, id: string}[]} privateServers */ export interface Wrapper { - readonly client: Client, - readonly prefix: string, - readonly commands: string - init?: (handler: Handler) => void, - readonly privateServers: { test: boolean, id: string }[], + readonly client: Client; + readonly prefix: string; + readonly commands: string; + init?: (handler: Handler) => void; + readonly privateServers: { test: boolean; id: string }[]; } /** - * An object that gets imported and acts as a command. + * An object that gets imported and acts as a command. * @typedef {object} Module * @property {string} desc * @property {Visibility} visibility @@ -266,13 +260,13 @@ export interface Wrapper { */ export interface Module { - alias: string[], - desc: string, - visibility: Visibility, - type: CommandType, - test : boolean, - delegate: (eventParams: Context, args: Ok) => Awaitable | void> - parse?: (ctx: Context, args: Arg) => Utils.ArgType + alias: string[]; + desc: string; + visibility: Visibility; + type: CommandType; + test: boolean; + delegate: (eventParams: Context, args: Ok) => Awaitable | void>; + parse?: (ctx: Context, args: Arg) => Utils.ArgType; } /** From 57392f4e6a8eb64adf3e62dee408e5b407f19803 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 09:43:38 +0530 Subject: [PATCH 02/10] made the code pretty :) --- src/handler/Utilities/Preprocessors/args.ts | 60 ++++++++++----------- src/handler/Utilities/messageHelpers.ts | 2 +- src/handler/Utilities/readFile.ts | 56 +++++++++---------- src/handler/logger.ts | 35 ++++++------ src/index.ts | 2 +- src/types/handler.ts | 16 +++--- 6 files changed, 82 insertions(+), 89 deletions(-) diff --git a/src/handler/Utilities/Preprocessors/args.ts b/src/handler/Utilities/Preprocessors/args.ts index 2cd02ab..8f94b0c 100644 --- a/src/handler/Utilities/Preprocessors/args.ts +++ b/src/handler/Utilities/Preprocessors/args.ts @@ -8,70 +8,70 @@ import type { possibleOutput } from '../../../types/handler'; export type ArgType = Result; /** - * - * @param {string} arg - command arguments + * + * @param {string} arg - command arguments * @param {possibleOutput} onFailure - if `Number.parseInt` returns NaN * @returns {ArgType} Attempts to use `Number.parseInt()` on `arg` */ export function parseInt(arg: string, onFailure: possibleOutput): ArgType { - const val = Number.parseInt(arg); + const val = Number.parseInt(arg); - return val === NaN ? Err(onFailure) : Ok(val); + return val === NaN ? Err(onFailure) : Ok(val); } /** - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - If cannot parse `arg` into boolean. - * @param { {yesRegex: RegExp, noRegex: RegExp} } regexes - default regexes: yes : `/^(?:y(?:es)?|👍)$/i`, no : /^(?:n(?:o)?|👎)$/i + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - If cannot parse `arg` into boolean. + * @param { {yesRegex: RegExp, noRegex: RegExp} } regexes - default regexes: yes : `/^(?:y(?:es)?|👍)$/i`, no : /^(?:n(?:o)?|👎)$/i * @returns { ArgType } attemps to parse `args` as a boolean */ export function parseBool( - arg: string, - onFailure: possibleOutput = `Cannot parse '${arg}' as a boolean`, - regexes: { - yesRegex: RegExp, - noRegex: RegExp - } = { - yesRegex: /^(?:y(?:es)?|👍)$/i, - noRegex: /^(?:n(?:o)?|👎)$/i - } + arg: string, + onFailure: possibleOutput = `Cannot parse '${arg}' as a boolean`, + regexes: { + yesRegex: RegExp; + noRegex: RegExp; + } = { + yesRegex: /^(?:y(?:es)?|👍)$/i, + noRegex: /^(?:n(?:o)?|👎)$/i, + }, ): ArgType { - if (arg.match(regexes.yesRegex)) return Ok(true); - if (arg.match(regexes.noRegex)) return Ok(false); + if (arg.match(regexes.yesRegex)) return Ok(true); + if (arg.match(regexes.noRegex)) return Ok(false); - return Err(onFailure); + return Err(onFailure); } /** - * - * @param {string} arg - command arguments + * + * @param {string} arg - command arguments * @param {string} sep - default separator = ' ' * @returns {Ok} */ export function toArr(arg: string, sep = ' '): ArgType { - return Ok(arg.split(sep)); + return Ok(arg.split(sep)); } /** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `Utils.parseInt` + * + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - delegates `Utils.parseInt` * @returns {ArgType} */ export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen(num => Ok(num > 0 ? num : -num)); + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); } /** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `parseInt` + * + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - delegates `parseInt` * @returns {ArgType} */ export function toNegativeInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen(num => Ok(num > 0 ? -num : num)); + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); } diff --git a/src/handler/Utilities/messageHelpers.ts b/src/handler/Utilities/messageHelpers.ts index cfad6dc..6050734 100644 --- a/src/handler/Utilities/messageHelpers.ts +++ b/src/handler/Utilities/messageHelpers.ts @@ -5,7 +5,7 @@ export function isBot(message: Message) { } export function hasPrefix(message: Message, prefix: string) { - return (message.content.slice(0, prefix.length).toLowerCase().trim()) === prefix; + return message.content.slice(0, prefix.length).toLowerCase().trim() === prefix; } export function fmt(msg: Message, prefix: string): string[] { diff --git a/src/handler/Utilities/readFile.ts b/src/handler/Utilities/readFile.ts index 9a6917f..f8d694b 100644 --- a/src/handler/Utilities/readFile.ts +++ b/src/handler/Utilities/readFile.ts @@ -1,23 +1,15 @@ -import type { - ApplicationCommandOptionData -} from 'discord.js'; +import type { ApplicationCommandOptionData } from 'discord.js'; -import { - readdirSync, - statSync -} from 'fs'; +import { readdirSync, statSync } from 'fs'; -import { - basename, - join -} from 'path'; +import { basename, join } from 'path'; import type * as Sern from '../sern'; export type CommandVal = { - mod: Sern.Module, - options: ApplicationCommandOptionData[], -} + mod: Sern.Module; + options: ApplicationCommandOptionData[]; +}; export const Commands = new Map(); export const Alias = new Map(); @@ -27,11 +19,9 @@ async function readPath(dir: string, arrayOfFiles: string[] = []): Promise n.substring(0, n.length - 3); /** - * @param {Sern.Handler} handler an instance of Sern.Handler + * @param {Sern.Handler} handler an instance of Sern.Handler * @returns {Promise<{ name: string; mod: Sern.Module; absPath: string; }[]>} data from command files -*/ + */ -export async function buildData(handler: Sern.Handler) - : Promise<{ - name: string; - mod: Sern.Module; - absPath: string; - }[]> { - const commandDir = handler.commandDir; - return Promise.all((await getCommands(commandDir)) - .map(async absPath => { - return { name: basename(absPath), mod: (await import(absPath)).default as Sern.Module, absPath } - })); +export async function buildData(handler: Sern.Handler): Promise< + { + name: string; + mod: Sern.Module; + absPath: string; + }[] +> { + const commandDir = handler.commandDir; + return Promise.all( + (await getCommands(commandDir)).map(async (absPath) => { + return { name: basename(absPath), mod: (await import(absPath)).default as Sern.Module, absPath }; + }), + ); } export async function getCommands(dir: string): Promise { diff --git a/src/handler/logger.ts b/src/handler/logger.ts index b5343dd..f4cc0bc 100644 --- a/src/handler/logger.ts +++ b/src/handler/logger.ts @@ -11,10 +11,11 @@ enum sEvent { } export default class Logger { - - public clear() { console.clear() } - - public log(e : T, message: string) { + public clear() { + console.clear(); + } + + public log(e: T, message: string) { dayJS.extend(UTC); dayJS.extend(Timezone); dayJS.tz.guess(); @@ -24,18 +25,18 @@ export default class Logger { } /** - * Utilizes console.table() to print out memory usage of current process. - * Optional at startup. - * + * Utilizes console.table() to print out memory usage of current process. + * Optional at startup. + * */ - - public tableRam() { - console.table( - Object.entries(process.memoryUsage()) - .map(([k, v]: [string, number]) => { - return { [k]: `${((Math.round(v) / 1024 / 1024 * 100) / 100).toFixed(2)} MBs` }; - }) - .reduce(((r, c) => Object.assign(r, c)), {}) - ); - } + + public tableRam() { + console.table( + Object.entries(process.memoryUsage()) + .map(([k, v]: [string, number]) => { + return { [k]: `${(((Math.round(v) / 1024 / 1024) * 100) / 100).toFixed(2)} MBs` }; + }) + .reduce((r, c) => Object.assign(r, c), {}), + ); + } } diff --git a/src/index.ts b/src/index.ts index 8de4cb0..6d1af5e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import * as Sern from './handler/sern'; -import * as Utils from './handler/utilities/preprocessors/args'; +import * as Utils from './handler/Utilities/Preprocessors/args'; import * as Types from './types/handler'; module.exports = { Sern, Utils, Types }; diff --git a/src/types/handler.ts b/src/types/handler.ts index d1797ad..340620f 100644 --- a/src/types/handler.ts +++ b/src/types/handler.ts @@ -5,30 +5,30 @@ import type { CommandInteractionOptionResolver, Message, MessagePayload, - MessageOptions + MessageOptions, } from 'discord.js'; import type * as Sern from '../handler/sern'; export type Visibility = 'private' | 'public'; // Anything that can be sent in a `#send` or `#reply` -export type possibleOutput = T | MessagePayload & MessageOptions; +export type possibleOutput = T | (MessagePayload & MessageOptions); export type Nullable = T | null; export type delegate = Sern.Module['delegate']; // Thanks @cursorsdottsx export type ParseType = { [K in keyof T]: T[K] extends unknown ? [k: K, args: T[K]] : never; -} [keyof T]; +}[keyof T]; // A Sern.Module['delegate'] will carry a Context Parameter export type Context = { - message: Option, - interaction: Option -} + message: Option; + interaction: Option; +}; + +export type Arg = ParseType<{ text: string; slash: SlashOptions }>; -export type Arg = ParseType<{ text: string, slash: SlashOptions }>; - // TypeAlias for interaction.options export type SlashOptions = Omit; From c9dfdb0467cbfb9d89d0b2c63a081f9f96a4e48b Mon Sep 17 00:00:00 2001 From: EvolutionX <85353424+EvolutionX-10@users.noreply.github.com> Date: Mon, 14 Feb 2022 09:48:16 +0530 Subject: [PATCH 03/10] Update .prettierrc --- .prettierrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.prettierrc b/.prettierrc index 3203c2e..2f31668 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,5 +3,5 @@ "trailingComma": "all", "singleQuote": true, "printWidth": 120, - "tabWidth": 2 -} \ No newline at end of file + "tabWidth": 4 +} From 38b478a9fb281aed969e5148b983163336e1e0b1 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 09:50:16 +0530 Subject: [PATCH 04/10] lowercased Utilities --- src/handler/sern.ts | 6 +++--- src/index.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handler/sern.ts b/src/handler/sern.ts index 0fa6c0a..e097ac2 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -1,12 +1,12 @@ -import * as Files from './Utilities/readFile'; -import type * as Utils from './Utilities/Preprocessors/args'; +import * as Files from './utilities/readFile'; +import type * as Utils from './utilities/Preprocessors/args'; import type { Arg, Context, Visibility, possibleOutput } from '../types/handler'; import type { ApplicationCommandOptionData, Awaitable, Client, CommandInteraction, Message } from 'discord.js'; import { Ok, Result, None, Some } from 'ts-results'; -import { isBot, hasPrefix, fmt } from './Utilities/messageHelpers'; +import { isBot, hasPrefix, fmt } from './utilities/messageHelpers'; import Logger from './logger'; /** diff --git a/src/index.ts b/src/index.ts index 6d1af5e..2568f39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import * as Sern from './handler/sern'; -import * as Utils from './handler/Utilities/Preprocessors/args'; +import * as Utils from './handler/utilities/Preprocessors/args'; import * as Types from './types/handler'; module.exports = { Sern, Utils, Types }; From 1c679bd341bbf56df4996e5029fc8aff3f4197f7 Mon Sep 17 00:00:00 2001 From: EvolutionX <85353424+EvolutionX-10@users.noreply.github.com> Date: Mon, 14 Feb 2022 09:51:51 +0530 Subject: [PATCH 05/10] Delete src/handler/Utilities directory --- src/handler/Utilities/Preprocessors/args.ts | 77 --------------------- src/handler/Utilities/messageHelpers.ts | 13 ---- src/handler/Utilities/readFile.ts | 56 --------------- 3 files changed, 146 deletions(-) delete mode 100644 src/handler/Utilities/Preprocessors/args.ts delete mode 100644 src/handler/Utilities/messageHelpers.ts delete mode 100644 src/handler/Utilities/readFile.ts diff --git a/src/handler/Utilities/Preprocessors/args.ts b/src/handler/Utilities/Preprocessors/args.ts deleted file mode 100644 index 8f94b0c..0000000 --- a/src/handler/Utilities/Preprocessors/args.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Err, Ok, Result } from 'ts-results'; -import type { possibleOutput } from '../../../types/handler'; - -/** - * Wrapper type taking `Ok(T)` or `Err(possibleOutput)` e.g `Result = Result; - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - if `Number.parseInt` returns NaN - * @returns {ArgType} Attempts to use `Number.parseInt()` on `arg` - */ - -export function parseInt(arg: string, onFailure: possibleOutput): ArgType { - const val = Number.parseInt(arg); - - return val === NaN ? Err(onFailure) : Ok(val); -} - -/** - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - If cannot parse `arg` into boolean. - * @param { {yesRegex: RegExp, noRegex: RegExp} } regexes - default regexes: yes : `/^(?:y(?:es)?|👍)$/i`, no : /^(?:n(?:o)?|👎)$/i - * @returns { ArgType } attemps to parse `args` as a boolean - */ - -export function parseBool( - arg: string, - onFailure: possibleOutput = `Cannot parse '${arg}' as a boolean`, - regexes: { - yesRegex: RegExp; - noRegex: RegExp; - } = { - yesRegex: /^(?:y(?:es)?|👍)$/i, - noRegex: /^(?:n(?:o)?|👎)$/i, - }, -): ArgType { - if (arg.match(regexes.yesRegex)) return Ok(true); - if (arg.match(regexes.noRegex)) return Ok(false); - - return Err(onFailure); -} - -/** - * - * @param {string} arg - command arguments - * @param {string} sep - default separator = ' ' - * @returns {Ok} - */ - -export function toArr(arg: string, sep = ' '): ArgType { - return Ok(arg.split(sep)); -} - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `Utils.parseInt` - * @returns {ArgType} - */ - -export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); -} - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `parseInt` - * @returns {ArgType} - */ -export function toNegativeInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); -} diff --git a/src/handler/Utilities/messageHelpers.ts b/src/handler/Utilities/messageHelpers.ts deleted file mode 100644 index 6050734..0000000 --- a/src/handler/Utilities/messageHelpers.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Message } from 'discord.js'; - -export function isBot(message: Message) { - return message.author.bot; -} - -export function hasPrefix(message: Message, prefix: string) { - return message.content.slice(0, prefix.length).toLowerCase().trim() === prefix; -} - -export function fmt(msg: Message, prefix: string): string[] { - return msg.content.slice(prefix.length).trim().split(/\s+/g); -} diff --git a/src/handler/Utilities/readFile.ts b/src/handler/Utilities/readFile.ts deleted file mode 100644 index f8d694b..0000000 --- a/src/handler/Utilities/readFile.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { ApplicationCommandOptionData } from 'discord.js'; - -import { readdirSync, statSync } from 'fs'; - -import { basename, join } from 'path'; - -import type * as Sern from '../sern'; - -export type CommandVal = { - mod: Sern.Module; - options: ApplicationCommandOptionData[]; -}; - -export const Commands = new Map(); -export const Alias = new Map(); - -// Courtesy of Townsy#0001 on Discord -async function readPath(dir: string, arrayOfFiles: string[] = []): Promise { - try { - const files = readdirSync(dir); - for (const file of files) { - if (statSync(dir + '/' + file).isDirectory()) await readPath(dir + '/' + file, arrayOfFiles); - else arrayOfFiles.push(join(dir, '/', file)); - } - } catch (err) { - throw err; - } - - return arrayOfFiles; -} - -export const fmtFileName = (n: string) => n.substring(0, n.length - 3); - -/** - * @param {Sern.Handler} handler an instance of Sern.Handler - * @returns {Promise<{ name: string; mod: Sern.Module; absPath: string; }[]>} data from command files - */ - -export async function buildData(handler: Sern.Handler): Promise< - { - name: string; - mod: Sern.Module; - absPath: string; - }[] -> { - const commandDir = handler.commandDir; - return Promise.all( - (await getCommands(commandDir)).map(async (absPath) => { - return { name: basename(absPath), mod: (await import(absPath)).default as Sern.Module, absPath }; - }), - ); -} - -export async function getCommands(dir: string): Promise { - return readPath(join(process.cwd(), dir)); -} From 252df8b99002d50cc2f70e3f7a6c2418f48a79e9 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 09:52:22 +0530 Subject: [PATCH 06/10] hmm --- src/handler/sern.ts | 6 +++--- src/handler/{Utilities => utilitiess}/Preprocessors/args.ts | 0 src/handler/{Utilities => utilitiess}/messageHelpers.ts | 0 src/handler/{Utilities => utilitiess}/readFile.ts | 0 src/index.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/handler/{Utilities => utilitiess}/Preprocessors/args.ts (100%) rename src/handler/{Utilities => utilitiess}/messageHelpers.ts (100%) rename src/handler/{Utilities => utilitiess}/readFile.ts (100%) diff --git a/src/handler/sern.ts b/src/handler/sern.ts index e097ac2..ba730f1 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -1,12 +1,12 @@ -import * as Files from './utilities/readFile'; -import type * as Utils from './utilities/Preprocessors/args'; +import * as Files from './utilitiess/readFile'; +import type * as Utils from './utilitiess/Preprocessors/args'; import type { Arg, Context, Visibility, possibleOutput } from '../types/handler'; import type { ApplicationCommandOptionData, Awaitable, Client, CommandInteraction, Message } from 'discord.js'; import { Ok, Result, None, Some } from 'ts-results'; -import { isBot, hasPrefix, fmt } from './utilities/messageHelpers'; +import { isBot, hasPrefix, fmt } from './utilitiess/messageHelpers'; import Logger from './logger'; /** diff --git a/src/handler/Utilities/Preprocessors/args.ts b/src/handler/utilitiess/Preprocessors/args.ts similarity index 100% rename from src/handler/Utilities/Preprocessors/args.ts rename to src/handler/utilitiess/Preprocessors/args.ts diff --git a/src/handler/Utilities/messageHelpers.ts b/src/handler/utilitiess/messageHelpers.ts similarity index 100% rename from src/handler/Utilities/messageHelpers.ts rename to src/handler/utilitiess/messageHelpers.ts diff --git a/src/handler/Utilities/readFile.ts b/src/handler/utilitiess/readFile.ts similarity index 100% rename from src/handler/Utilities/readFile.ts rename to src/handler/utilitiess/readFile.ts diff --git a/src/index.ts b/src/index.ts index 2568f39..8320b66 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import * as Sern from './handler/sern'; -import * as Utils from './handler/utilities/Preprocessors/args'; +import * as Utils from './handler/utilitiess/Preprocessors/args'; import * as Types from './types/handler'; module.exports = { Sern, Utils, Types }; From 69a8baf201b21b2716e3f575dab2f079dce57a3a Mon Sep 17 00:00:00 2001 From: EvolutionX <85353424+EvolutionX-10@users.noreply.github.com> Date: Mon, 14 Feb 2022 09:55:15 +0530 Subject: [PATCH 07/10] Delete src/handler/utilities/Preprocessors directory --- src/handler/utilities/Preprocessors/args.ts | 77 --------------------- 1 file changed, 77 deletions(-) delete mode 100644 src/handler/utilities/Preprocessors/args.ts diff --git a/src/handler/utilities/Preprocessors/args.ts b/src/handler/utilities/Preprocessors/args.ts deleted file mode 100644 index 8f94b0c..0000000 --- a/src/handler/utilities/Preprocessors/args.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Err, Ok, Result } from 'ts-results'; -import type { possibleOutput } from '../../../types/handler'; - -/** - * Wrapper type taking `Ok(T)` or `Err(possibleOutput)` e.g `Result = Result; - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - if `Number.parseInt` returns NaN - * @returns {ArgType} Attempts to use `Number.parseInt()` on `arg` - */ - -export function parseInt(arg: string, onFailure: possibleOutput): ArgType { - const val = Number.parseInt(arg); - - return val === NaN ? Err(onFailure) : Ok(val); -} - -/** - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - If cannot parse `arg` into boolean. - * @param { {yesRegex: RegExp, noRegex: RegExp} } regexes - default regexes: yes : `/^(?:y(?:es)?|👍)$/i`, no : /^(?:n(?:o)?|👎)$/i - * @returns { ArgType } attemps to parse `args` as a boolean - */ - -export function parseBool( - arg: string, - onFailure: possibleOutput = `Cannot parse '${arg}' as a boolean`, - regexes: { - yesRegex: RegExp; - noRegex: RegExp; - } = { - yesRegex: /^(?:y(?:es)?|👍)$/i, - noRegex: /^(?:n(?:o)?|👎)$/i, - }, -): ArgType { - if (arg.match(regexes.yesRegex)) return Ok(true); - if (arg.match(regexes.noRegex)) return Ok(false); - - return Err(onFailure); -} - -/** - * - * @param {string} arg - command arguments - * @param {string} sep - default separator = ' ' - * @returns {Ok} - */ - -export function toArr(arg: string, sep = ' '): ArgType { - return Ok(arg.split(sep)); -} - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `Utils.parseInt` - * @returns {ArgType} - */ - -export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); -} - -/** - * - * @param {string} arg - command arguments - * @param {possibleOutput} onFailure - delegates `parseInt` - * @returns {ArgType} - */ -export function toNegativeInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); -} From ab7c5e09ee72f3405ad87869bf80efb9f1391f80 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 09:55:24 +0530 Subject: [PATCH 08/10] lowercased preprocessors --- src/handler/sern.ts | 2 +- src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handler/sern.ts b/src/handler/sern.ts index e097ac2..1611a57 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -1,5 +1,5 @@ import * as Files from './utilities/readFile'; -import type * as Utils from './utilities/Preprocessors/args'; +import type * as Utils from './utilities/preprocessors/args'; import type { Arg, Context, Visibility, possibleOutput } from '../types/handler'; diff --git a/src/index.ts b/src/index.ts index 2568f39..8de4cb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import * as Sern from './handler/sern'; -import * as Utils from './handler/utilities/Preprocessors/args'; +import * as Utils from './handler/utilities/preprocessors/args'; import * as Types from './types/handler'; module.exports = { Sern, Utils, Types }; From 5102563869d92c1dd5a22e28ac29b1d2b0097e58 Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 13:09:35 +0530 Subject: [PATCH 09/10] added preprocessors --- src/handler/utilities/preprocessors/args.ts | 77 +++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/handler/utilities/preprocessors/args.ts diff --git a/src/handler/utilities/preprocessors/args.ts b/src/handler/utilities/preprocessors/args.ts new file mode 100644 index 0000000..ad1dfdd --- /dev/null +++ b/src/handler/utilities/preprocessors/args.ts @@ -0,0 +1,77 @@ +import { Err, Ok, Result } from 'ts-results'; +import type { possibleOutput } from '../../../types/handler'; + +/** + * Wrapper type taking `Ok(T)` or `Err(possibleOutput)` e.g `Result = Result; + +/** + * + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - if `Number.parseInt` returns NaN + * @returns {ArgType} Attempts to use `Number.parseInt()` on `arg` + */ + +export function parseInt(arg: string, onFailure: possibleOutput): ArgType { + const val = Number.parseInt(arg); + + return val === NaN ? Err(onFailure) : Ok(val); +} + +/** + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - If cannot parse `arg` into boolean. + * @param { {yesRegex: RegExp, noRegex: RegExp} } regexes - default regexes: yes : `/^(?:y(?:es)?|👍)$/i`, no : /^(?:n(?:o)?|👎)$/i + * @returns { ArgType } attemps to parse `args` as a boolean + */ + +export function parseBool( + arg: string, + onFailure: possibleOutput = `Cannot parse '${arg}' as a boolean`, + regexes: { + yesRegex: RegExp; + noRegex: RegExp; + } = { + yesRegex: /^(?:y(?:es)?|👍)$/i, + noRegex: /^(?:n(?:o)?|👎)$/i, + }, +): ArgType { + if (arg.match(regexes.yesRegex)) return Ok(true); + if (arg.match(regexes.noRegex)) return Ok(false); + + return Err(onFailure); +} + +/** + * + * @param {string} arg - command arguments + * @param {string} sep - default separator = ' ' + * @returns {Ok} + */ + +export function toArr(arg: string, sep = ' '): ArgType { + return Ok(arg.split(sep)); +} + +/** + * + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - delegates `Utils.parseInt` + * @returns {ArgType} + */ + +export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType { + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); +} + +/** + * + * @param {string} arg - command arguments + * @param {possibleOutput} onFailure - delegates `parseInt` + * @returns {ArgType} + */ +export function toNegativeInt(arg: string, onFailure: possibleOutput): ArgType { + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); +} \ No newline at end of file From a648f347f129dc0e3eef57b9d7348022d7dde20e Mon Sep 17 00:00:00 2001 From: EvolutionX Date: Mon, 14 Feb 2022 13:18:14 +0530 Subject: [PATCH 10/10] finished formatting --- .eslintrc | 5 +- src/handler/logger.ts | 60 +-- src/handler/sern.ts | 408 ++++++++++---------- src/handler/utilities/messageHelpers.ts | 6 +- src/handler/utilities/preprocessors/args.ts | 36 +- src/handler/utilities/readFile.ts | 46 +-- src/types/handler.ts | 16 +- 7 files changed, 289 insertions(+), 288 deletions(-) diff --git a/.eslintrc b/.eslintrc index 6598da8..c142c1f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,9 +1,10 @@ { "parser": "@typescript-eslint/parser", "extends": ["plugin:@typescript-eslint/recommended"], - "parserOptions": { "ecmaVersion": "esnext", "sourceType": "" }, + "parserOptions": { "ecmaVersion": "latest", "sourceType": "script" }, "rules": { "@typescript-eslint/no-non-null-assertion": "off", - "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals" : true }] + "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals" : true }], + "semi": ["error", "always"] } } \ No newline at end of file diff --git a/src/handler/logger.ts b/src/handler/logger.ts index f4cc0bc..8fd122d 100644 --- a/src/handler/logger.ts +++ b/src/handler/logger.ts @@ -3,40 +3,40 @@ import Timezone from 'dayjs/plugin/timezone'; import UTC from 'dayjs/plugin/timezone'; enum sEvent { - GLOBAL_SLASH, - LOCAL_SLASH, - DM, - CRASH, - TEXT_CMD, + GLOBAL_SLASH, + LOCAL_SLASH, + DM, + CRASH, + TEXT_CMD, } export default class Logger { - public clear() { - console.clear(); - } + public clear() { + console.clear(); + } - public log(e: T, message: string) { - dayJS.extend(UTC); - dayJS.extend(Timezone); - dayJS.tz.guess(); - // add colored logging? - const tz = dayJS().format(); - console.log(`[${`${tz}`}][${sEvent[e]}] :: ${message}`); - } + public log(e: T, message: string) { + dayJS.extend(UTC); + dayJS.extend(Timezone); + dayJS.tz.guess(); + // add colored logging? + const tz = dayJS().format(); + console.log(`[${`${tz}`}][${sEvent[e]}] :: ${message}`); + } - /** - * Utilizes console.table() to print out memory usage of current process. - * Optional at startup. - * - */ + /** + * Utilizes console.table() to print out memory usage of current process. + * Optional at startup. + * + */ - public tableRam() { - console.table( - Object.entries(process.memoryUsage()) - .map(([k, v]: [string, number]) => { - return { [k]: `${(((Math.round(v) / 1024 / 1024) * 100) / 100).toFixed(2)} MBs` }; - }) - .reduce((r, c) => Object.assign(r, c), {}), - ); - } + public tableRam() { + console.table( + Object.entries(process.memoryUsage()) + .map(([k, v]: [string, number]) => { + return { [k]: `${(((Math.round(v) / 1024 / 1024) * 100) / 100).toFixed(2)} MBs` }; + }) + .reduce((r, c) => Object.assign(r, c), {}), + ); + } } diff --git a/src/handler/sern.ts b/src/handler/sern.ts index 1611a57..e8a8feb 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -14,222 +14,222 @@ import Logger from './logger'; */ export class Handler { - private wrapper: Wrapper; + private wrapper: Wrapper; - /** - * @constructor - * @param {Wrapper} wrapper The data that is required to run sern handler - */ + /** + * @constructor + * @param {Wrapper} wrapper The data that is required to run sern handler + */ - constructor(wrapper: Wrapper) { - this.wrapper = wrapper; - this.client + constructor(wrapper: Wrapper) { + this.wrapper = wrapper; + this.client - /** - * On ready, builds command data and registers them all - * from command directory - **/ + /** + * On ready, builds command data and registers them all + * from command directory + **/ - .on('ready', async () => { - Files.buildData(this).then((data) => this.registerModules(data)); - if (wrapper.init !== undefined) wrapper.init(this); - new Logger().tableRam(); - }) + .on('ready', async () => { + Files.buildData(this).then((data) => this.registerModules(data)); + if (wrapper.init !== undefined) wrapper.init(this); + new Logger().tableRam(); + }) - .on('messageCreate', async (message: Message) => { - if (isBot(message) || !hasPrefix(message, this.prefix)) return; - if (message.channel.type === 'DM') return; // TODO: Handle dms + .on('messageCreate', async (message: Message) => { + if (isBot(message) || !hasPrefix(message, this.prefix)) return; + if (message.channel.type === 'DM') return; // TODO: Handle dms - const tryFmt = fmt(message, this.prefix); - const commandName = tryFmt.shift()!; - const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName); - if (module === undefined) { - message.channel.send('Unknown legacy command'); - return; - } - const cmdResult = await this.commandResult(module, message, tryFmt.join(' ')); - if (cmdResult === undefined) return; + const tryFmt = fmt(message, this.prefix); + const commandName = tryFmt.shift()!; + const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName); + if (module === undefined) { + message.channel.send('Unknown legacy command'); + return; + } + const cmdResult = await this.commandResult(module, message, tryFmt.join(' ')); + if (cmdResult === undefined) return; - message.channel.send(cmdResult); - }) + message.channel.send(cmdResult); + }) - .on('interactionCreate', async (interaction) => { - if (!interaction.isCommand()) return; - const module = Files.Commands.get(interaction.commandName); - const res = await this.interactionResult(module, interaction); - if (res === undefined) return; - await interaction.reply(res); - }); - } - - /** - * - * @param {Files.CommandVal | undefined} module Command file information - * @param {CommandInteraction} interaction The Discord.js command interaction (DiscordJS#CommandInteraction)) - * @returns {possibleOutput | undefined} Takes return value and replies it, if possible input - */ - - private async interactionResult( - module: Files.CommandVal | undefined, - interaction: CommandInteraction, - ): Promise { - if (module === undefined) return 'Unknown slash command!'; - const name = Array.from(Files.Commands.keys()).find((it) => it === interaction.commandName); - if (name === undefined) return `Could not find ${interaction.commandName} command!`; - - if (module.mod.type < CommandType.SLASH) return 'This is not a slash command'; - - const context = { message: None, interaction: Some(interaction) }; - const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok(''); - - if (parsedArgs.err) return parsedArgs.val; - - return (await module.mod.delegate(context, parsedArgs))?.val; - } - - /** - * - * @param {Files.CommandVal | undefined} module Command file information - * @param {Message} message The message object - * @param {string} args Anything after the command - * @returns Takes return value and replies it, if possible input - */ - - private async commandResult( - module: Files.CommandVal | undefined, - message: Message, - args: string, - ): Promise { - if (module?.mod === undefined) return 'Unknown legacy command'; - if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command`; - if (module.mod.visibility === 'private') { - const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test; - if (checkIsTestServer === undefined) - return 'This command has the private modifier but is not registered under Handler#privateServers'; - if (checkIsTestServer !== module.mod.test) { - const msg = `This command is only available on test servers.`; // TODO: Customizable private message - - return msg; - } + .on('interactionCreate', async (interaction) => { + if (!interaction.isCommand()) return; + const module = Files.Commands.get(interaction.commandName); + const res = await this.interactionResult(module, interaction); + if (res === undefined) return; + await interaction.reply(res); + }); } - const context = { - message: Some(message), - interaction: None, - }; - const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok(''); - if (parsedArgs.err) return parsedArgs.val; - return (await module.mod.delegate(context, parsedArgs))?.val; - } - /** - * This function chains `Files.buildData` - * @param {{name: string, mod: Module, absPath: string}} modArr module information - */ + /** + * + * @param {Files.CommandVal | undefined} module Command file information + * @param {CommandInteraction} interaction The Discord.js command interaction (DiscordJS#CommandInteraction)) + * @returns {possibleOutput | undefined} Takes return value and replies it, if possible input + */ - private async registerModules( - modArr: { - name: string; - mod: Module; - absPath: string; - }[], - ) { - for await (const { name, mod, absPath } of modArr) { - const cmdName = Files.fmtFileName(name); - switch (mod.type) { - case 1: - Files.Commands.set(cmdName, { mod, options: [] }); - break; - case 2: - case 1 | 2: - { - const options = (await import(absPath)).options as ApplicationCommandOptionData[]; - Files.Commands.set(cmdName, { mod, options: options ?? [] }); - switch (mod.visibility) { - case 'private': { - // Reloading guild slash commands - await this.reloadSlash(cmdName, mod.desc, options); - } - case 'public': { - // Creating global commands - await this.client.application!.commands.create({ - name: cmdName, - description: mod.desc, - options, - }); - } + private async interactionResult( + module: Files.CommandVal | undefined, + interaction: CommandInteraction, + ): Promise { + if (module === undefined) return 'Unknown slash command!'; + const name = Array.from(Files.Commands.keys()).find((it) => it === interaction.commandName); + if (name === undefined) return `Could not find ${interaction.commandName} command!`; + + if (module.mod.type < CommandType.SLASH) return 'This is not a slash command'; + + const context = { message: None, interaction: Some(interaction) }; + const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok(''); + + if (parsedArgs.err) return parsedArgs.val; + + return (await module.mod.delegate(context, parsedArgs))?.val; + } + + /** + * + * @param {Files.CommandVal | undefined} module Command file information + * @param {Message} message The message object + * @param {string} args Anything after the command + * @returns Takes return value and replies it, if possible input + */ + + private async commandResult( + module: Files.CommandVal | undefined, + message: Message, + args: string, + ): Promise { + if (module?.mod === undefined) return 'Unknown legacy command'; + if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command`; + if (module.mod.visibility === 'private') { + const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test; + if (checkIsTestServer === undefined) + return 'This command has the private modifier but is not registered under Handler#privateServers'; + if (checkIsTestServer !== module.mod.test) { + const msg = `This command is only available on test servers.`; // TODO: Customizable private message + + return msg; } - } - break; - default: - throw Error(`SernHandlerError: ${name} with ${mod.visibility} is not a valid module type.`); - } - - if (mod.alias.length > 0) { - for (const alias of mod.alias) { - Files.Alias.set(alias, { mod, options: [] }); } - } + const context = { + message: Some(message), + interaction: None, + }; + const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok(''); + if (parsedArgs.err) return parsedArgs.val; + return (await module.mod.delegate(context, parsedArgs))?.val; } - } - /** - * - * @param {string} cmdName name of command - * @param {string} description description of command - * @param {ApplicationCommandOptionData[]} options any options for the slash command - */ + /** + * This function chains `Files.buildData` + * @param {{name: string, mod: Module, absPath: string}} modArr module information + */ - private async reloadSlash( - cmdName: string, - description: string, - options: ApplicationCommandOptionData[], - ): Promise { - for (const { id } of this.privateServers) { - const guild = await this.client.guilds.fetch(id); + private async registerModules( + modArr: { + name: string; + mod: Module; + absPath: string; + }[], + ) { + for await (const { name, mod, absPath } of modArr) { + const cmdName = Files.fmtFileName(name); + switch (mod.type) { + case 1: + Files.Commands.set(cmdName, { mod, options: [] }); + break; + case 2: + case 1 | 2: + { + const options = (await import(absPath)).options as ApplicationCommandOptionData[]; + Files.Commands.set(cmdName, { mod, options: options ?? [] }); + switch (mod.visibility) { + case 'private': { + // Reloading guild slash commands + await this.reloadSlash(cmdName, mod.desc, options); + } + case 'public': { + // Creating global commands + await this.client.application!.commands.create({ + name: cmdName, + description: mod.desc, + options, + }); + } + } + } + break; + default: + throw Error(`SernHandlerError: ${name} with ${mod.visibility} is not a valid module type.`); + } - guild.commands.create({ - name: cmdName, - description, - options, - }); + if (mod.alias.length > 0) { + for (const alias of mod.alias) { + Files.Alias.set(alias, { mod, options: [] }); + } + } + } } - } - /** - * @readonly - * @returns {string} The prefix used for legacy commands - */ + /** + * + * @param {string} cmdName name of command + * @param {string} description description of command + * @param {ApplicationCommandOptionData[]} options any options for the slash command + */ - get prefix(): string { - return this.wrapper.prefix; - } + private async reloadSlash( + cmdName: string, + description: string, + options: ApplicationCommandOptionData[], + ): Promise { + for (const { id } of this.privateServers) { + const guild = await this.client.guilds.fetch(id); - /** - * @readonly - * @returns {string} Directory of the commands folder - */ + guild.commands.create({ + name: cmdName, + description, + options, + }); + } + } - get commandDir(): string { - return this.wrapper.commands; - } + /** + * @readonly + * @returns {string} The prefix used for legacy commands + */ - /** - * @readonly - * @returns {Client} the discord.js client (DiscordJS#Client)); - */ + get prefix(): string { + return this.wrapper.prefix; + } - get client(): Client { - return this.wrapper.client; - } + /** + * @readonly + * @returns {string} Directory of the commands folder + */ - /** - * @readonly - * @returns {{test: boolean, id: string}[]} Private server ID for testing or personal use - */ + get commandDir(): string { + return this.wrapper.commands; + } - get privateServers(): { test: boolean; id: string }[] { - return this.wrapper.privateServers; - } + /** + * @readonly + * @returns {Client} the discord.js client (DiscordJS#Client)); + */ + + get client(): Client { + return this.wrapper.client; + } + + /** + * @readonly + * @returns {{test: boolean, id: string}[]} Private server ID for testing or personal use + */ + + get privateServers(): { test: boolean; id: string }[] { + return this.wrapper.privateServers; + } } /** @@ -242,11 +242,11 @@ export class Handler { * @property {readonly {test: boolean, id: string}[]} privateServers */ export interface Wrapper { - readonly client: Client; - readonly prefix: string; - readonly commands: string; - init?: (handler: Handler) => void; - readonly privateServers: { test: boolean; id: string }[]; + readonly client: Client; + readonly prefix: string; + readonly commands: string; + init?: (handler: Handler) => void; + readonly privateServers: { test: boolean; id: string }[]; } /** @@ -260,13 +260,13 @@ export interface Wrapper { */ export interface Module { - alias: string[]; - desc: string; - visibility: Visibility; - type: CommandType; - test: boolean; - delegate: (eventParams: Context, args: Ok) => Awaitable | void>; - parse?: (ctx: Context, args: Arg) => Utils.ArgType; + alias: string[]; + desc: string; + visibility: Visibility; + type: CommandType; + test: boolean; + delegate: (eventParams: Context, args: Ok) => Awaitable | void>; + parse?: (ctx: Context, args: Arg) => Utils.ArgType; } /** @@ -274,6 +274,6 @@ export interface Module { */ export enum CommandType { - TEXT = 1, - SLASH = 2, + TEXT = 1, + SLASH = 2, } diff --git a/src/handler/utilities/messageHelpers.ts b/src/handler/utilities/messageHelpers.ts index 6050734..85d43cf 100644 --- a/src/handler/utilities/messageHelpers.ts +++ b/src/handler/utilities/messageHelpers.ts @@ -1,13 +1,13 @@ import type { Message } from 'discord.js'; export function isBot(message: Message) { - return message.author.bot; + return message.author.bot; } export function hasPrefix(message: Message, prefix: string) { - return message.content.slice(0, prefix.length).toLowerCase().trim() === prefix; + return message.content.slice(0, prefix.length).toLowerCase().trim() === prefix; } export function fmt(msg: Message, prefix: string): string[] { - return msg.content.slice(prefix.length).trim().split(/\s+/g); + return msg.content.slice(prefix.length).trim().split(/\s+/g); } diff --git a/src/handler/utilities/preprocessors/args.ts b/src/handler/utilities/preprocessors/args.ts index ad1dfdd..722baba 100644 --- a/src/handler/utilities/preprocessors/args.ts +++ b/src/handler/utilities/preprocessors/args.ts @@ -15,9 +15,9 @@ export type ArgType = Result; */ export function parseInt(arg: string, onFailure: possibleOutput): ArgType { - const val = Number.parseInt(arg); + const val = Number.parseInt(arg); - return val === NaN ? Err(onFailure) : Ok(val); + return val === NaN ? Err(onFailure) : Ok(val); } /** @@ -28,20 +28,20 @@ export function parseInt(arg: string, onFailure: possibleOutput): ArgType { - if (arg.match(regexes.yesRegex)) return Ok(true); - if (arg.match(regexes.noRegex)) return Ok(false); + if (arg.match(regexes.yesRegex)) return Ok(true); + if (arg.match(regexes.noRegex)) return Ok(false); - return Err(onFailure); + return Err(onFailure); } /** @@ -52,7 +52,7 @@ export function parseBool( */ export function toArr(arg: string, sep = ' '): ArgType { - return Ok(arg.split(sep)); + return Ok(arg.split(sep)); } /** @@ -63,7 +63,7 @@ export function toArr(arg: string, sep = ' '): ArgType { */ export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? num : -num)); } /** @@ -73,5 +73,5 @@ export function toPositiveInt(arg: string, onFailure: possibleOutput): ArgType} */ export function toNegativeInt(arg: string, onFailure: possibleOutput): ArgType { - return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); -} \ No newline at end of file + return parseInt(arg, onFailure).andThen((num) => Ok(num > 0 ? -num : num)); +} diff --git a/src/handler/utilities/readFile.ts b/src/handler/utilities/readFile.ts index f8d694b..981e64b 100644 --- a/src/handler/utilities/readFile.ts +++ b/src/handler/utilities/readFile.ts @@ -7,8 +7,8 @@ import { basename, join } from 'path'; import type * as Sern from '../sern'; export type CommandVal = { - mod: Sern.Module; - options: ApplicationCommandOptionData[]; + mod: Sern.Module; + options: ApplicationCommandOptionData[]; }; export const Commands = new Map(); @@ -16,17 +16,17 @@ export const Alias = new Map(); // Courtesy of Townsy#0001 on Discord async function readPath(dir: string, arrayOfFiles: string[] = []): Promise { - try { - const files = readdirSync(dir); - for (const file of files) { - if (statSync(dir + '/' + file).isDirectory()) await readPath(dir + '/' + file, arrayOfFiles); - else arrayOfFiles.push(join(dir, '/', file)); + try { + const files = readdirSync(dir); + for (const file of files) { + if (statSync(dir + '/' + file).isDirectory()) await readPath(dir + '/' + file, arrayOfFiles); + else arrayOfFiles.push(join(dir, '/', file)); + } + } catch (err) { + throw err; } - } catch (err) { - throw err; - } - return arrayOfFiles; + return arrayOfFiles; } export const fmtFileName = (n: string) => n.substring(0, n.length - 3); @@ -37,20 +37,20 @@ export const fmtFileName = (n: string) => n.substring(0, n.length - 3); */ export async function buildData(handler: Sern.Handler): Promise< - { - name: string; - mod: Sern.Module; - absPath: string; - }[] + { + name: string; + mod: Sern.Module; + absPath: string; + }[] > { - const commandDir = handler.commandDir; - return Promise.all( - (await getCommands(commandDir)).map(async (absPath) => { - return { name: basename(absPath), mod: (await import(absPath)).default as Sern.Module, absPath }; - }), - ); + const commandDir = handler.commandDir; + return Promise.all( + (await getCommands(commandDir)).map(async (absPath) => { + return { name: basename(absPath), mod: (await import(absPath)).default as Sern.Module, absPath }; + }), + ); } export async function getCommands(dir: string): Promise { - return readPath(join(process.cwd(), dir)); + return readPath(join(process.cwd(), dir)); } diff --git a/src/types/handler.ts b/src/types/handler.ts index 340620f..0a79e85 100644 --- a/src/types/handler.ts +++ b/src/types/handler.ts @@ -1,11 +1,11 @@ import type { Option } from 'ts-results'; import type { - CommandInteraction, - CommandInteractionOptionResolver, - Message, - MessagePayload, - MessageOptions, + CommandInteraction, + CommandInteractionOptionResolver, + Message, + MessagePayload, + MessageOptions, } from 'discord.js'; import type * as Sern from '../handler/sern'; @@ -18,14 +18,14 @@ export type delegate = Sern.Module['delegate']; // Thanks @cursorsdottsx export type ParseType = { - [K in keyof T]: T[K] extends unknown ? [k: K, args: T[K]] : never; + [K in keyof T]: T[K] extends unknown ? [k: K, args: T[K]] : never; }[keyof T]; // A Sern.Module['delegate'] will carry a Context Parameter export type Context = { - message: Option; - interaction: Option; + message: Option; + interaction: Option; }; export type Arg = ParseType<{ text: string; slash: SlashOptions }>;