From 271046d43869415707eec27471f739504961da70 Mon Sep 17 00:00:00 2001 From: spuckhafte Date: Mon, 11 Sep 2023 14:35:49 +0530 Subject: [PATCH] ::new:: => generics for properly inferring types --- README.md | 13 ++++++++++--- dist/helpers/command.d.ts | 6 +++--- dist/helpers/funcs.d.ts | 5 ++++- dist/helpers/funcs.js | 31 +++++++++++++++++++++++++++++++ dist/index.d.ts | 6 ++---- dist/index.js | 27 +++++---------------------- package.json | 2 +- src/helpers/command.ts | 12 ++++++------ src/helpers/funcs.ts | 13 ++++++++++++- src/index.ts | 24 ++++++++++-------------- 10 files changed, 84 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 268a41c..fe859d7 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Fill up some details and you're ready to *go()* !
```js import { Command } from 'breezer.js/dist' -export default class extends Command { +export default class extends Command<[]> { constructor() { super({ structure: [], @@ -67,9 +67,14 @@ export default class extends Command { } ``` Every command is a class, child of the `Command` class.
+**Generic of class `Command<[]>`:** Defines the *type* of the structure of your cmd. + - helps in inferring the types of different fields returned by `this.extract()` *(explained later)* `structure` - defines a structure for your command. Helpful when extracting the fields of you commands.
This property will be better understood in the next command. Here there are no fields for this cmd => no structure.
+ +*Reason why we define the structure twice is because, `Generic` helps in inferring the type properly and the property `structure` helps in actual conversion of field (say to number) and raising errors in `strict` mode. + `strict` - set it to true if you want to recieve errors/warnings when: - user does not follow the defined structure of the cmd - a deleted msg listens for certain state(s) @@ -84,7 +89,7 @@ This feature should only be turned on during development. ```js import { Command } from 'breezer.js' -export default class extends Command { +export default class extends Command<[string]> { constructor() { super({ structure: ['string'], @@ -93,6 +98,7 @@ export default class extends Command { } async execute() { + // operation: string const [operation] = this.extract(); let value; try { @@ -137,7 +143,7 @@ import { MessageEmbed } from 'discord.js'; const state = new StateManager({ count: 1 }); -export default class extends Command { +export default class extends Command<[number]> { constructor() { super({ name: 'mul', @@ -148,6 +154,7 @@ export default class extends Command { }); } async execute() { + // by: number const [by] = this.extract(); await this.send({ diff --git a/dist/helpers/command.d.ts b/dist/helpers/command.d.ts index 7dfb3d4..c71e578 100644 --- a/dist/helpers/command.d.ts +++ b/dist/helpers/command.d.ts @@ -1,7 +1,7 @@ import { Message, PermissionResolvable } from "discord.js"; import { CmdStructure, CommandSettings, Payload } from "../../types"; import { StateManager } from "./stateManager.js"; -export declare class Command { +export declare class Command { structure: CmdStructure[]; name?: string; strict: boolean; @@ -13,7 +13,7 @@ export declare class Command { till?: 'forever' | number; constructor(settings: CommandSettings); /**Extract fields from a command as per their defined structure */ - extract(): (string | number)[]; + extract(): Structure; /**Add your logics for the command inside this function */ execute(): Promise; /**Send using this if there are states to manage */ @@ -23,7 +23,7 @@ export declare class Command { /**Check if the bot has a specific perm */ botHasPerm(perm: PermissionResolvable): boolean | undefined; } -export declare class TypicalCommand extends Command { +export declare class TypicalCommand extends Command { constructor(); execute(): Promise; } diff --git a/dist/helpers/funcs.d.ts b/dist/helpers/funcs.d.ts index a744434..22b96c5 100644 --- a/dist/helpers/funcs.d.ts +++ b/dist/helpers/funcs.d.ts @@ -1,4 +1,4 @@ -import { Message } from "discord.js"; +import { Message, PermissionResolvable } from "discord.js"; export declare function err(desc: string, cmd?: string, warn?: boolean): string; /**Listen to button interactions * @param users - array of user-ids who can click on button, empty array => anyone can click @@ -10,3 +10,6 @@ export declare function buttonSignal(users: string[], msg: Message | undefined, max?: number; time?: number; }): import("discord.js").InteractionCollector> | undefined; +/**Check if a user has a specific perm */ +export declare function userHasPerm(perm: PermissionResolvable, userId: string, msg: Message): Promise; +export declare function getIntents(): number[]; diff --git a/dist/helpers/funcs.js b/dist/helpers/funcs.js index 4f936bf..7079f1f 100644 --- a/dist/helpers/funcs.js +++ b/dist/helpers/funcs.js @@ -1,3 +1,13 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import { Intents } from "discord.js"; export function err(desc, cmd, warn = false) { return `[${warn ? 'warn' : 'err'}][cmd: ${cmd}] ${desc}`; } @@ -20,3 +30,24 @@ export function buttonSignal(users, msg, props) { }); return collector; } +/**Check if a user has a specific perm */ +export function userHasPerm(perm, userId, msg) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const channel = msg.channel; + const user = yield ((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.members.fetch(userId)); + if (!user) + return false; + return channel.permissionsFor(user).has(perm); + }); +} +export function getIntents() { + return [ + Intents.FLAGS.GUILD_MESSAGES, + Intents.FLAGS.GUILD_MESSAGE_REACTIONS, + Intents.FLAGS.MESSAGE_CONTENT, + Intents.FLAGS.GUILDS, + Intents.FLAGS.GUILD_MEMBERS, + Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS + ]; +} diff --git a/dist/index.d.ts b/dist/index.d.ts index ba273bb..e5b42c4 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,7 +1,7 @@ -import discord, { Message, PermissionResolvable } from 'discord.js'; +import discord from 'discord.js'; import { Command } from './helpers/command.js'; import { StateManager } from './helpers/stateManager.js'; -import { buttonSignal } from './helpers/funcs.js'; +import { buttonSignal, userHasPerm } from './helpers/funcs.js'; declare class Bot { commands: string[]; bot: discord.Client; @@ -18,6 +18,4 @@ declare class Bot { }); go(cb?: CallableFunction): Promise; } -/**Check if a user has a specific perm */ -declare function userHasPerm(perm: PermissionResolvable, userId: string, msg: Message): Promise; export { Bot, Command, StateManager, buttonSignal, userHasPerm }; diff --git a/dist/index.js b/dist/index.js index 243675f..583e3ea 100644 --- a/dist/index.js +++ b/dist/index.js @@ -7,22 +7,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -import discord, { Intents } from 'discord.js'; +import discord from 'discord.js'; import fs from 'fs'; import { revealNameOfCmd } from './helpers/handlers.js'; import { Command } from './helpers/command.js'; import { StateManager } from './helpers/stateManager.js'; -import { buttonSignal } from './helpers/funcs.js'; +import { buttonSignal, getIntents, userHasPerm } from './helpers/funcs.js'; class Bot { constructor(options) { - let intents = [ - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILD_MESSAGE_REACTIONS, - Intents.FLAGS.MESSAGE_CONTENT, - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MEMBERS, - Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS - ]; + let intents = getIntents(); this.commands = fs.readdirSync(options.commandsFolder).map(i => i.replace(options.lang, '')); this.token = options.token; this.cmdFolder = options.commandsFolder; @@ -31,7 +24,8 @@ class Bot { this.bot = new discord.Client({ intents }); let cmdsCollectd = {}; for (let command of this.commands) { - import(`file://${process.cwd()}/${this.cmdFolder}/${command}${this.lang}`).then(cmd => { + const cmdPath = `file://${process.cwd()}/${this.cmdFolder}/${command}${this.lang}`; + import(cmdPath).then(cmd => { if (cmd.default) cmdsCollectd[command] = cmd.default; }).catch(e => console.log(e)); @@ -57,15 +51,4 @@ class Bot { }); } } -/**Check if a user has a specific perm */ -function userHasPerm(perm, userId, msg) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const channel = msg.channel; - const user = yield ((_a = msg.guild) === null || _a === void 0 ? void 0 : _a.members.fetch(userId)); - if (!user) - return false; - return channel.permissionsFor(user).has(perm); - }); -} export { Bot, Command, StateManager, buttonSignal, userHasPerm }; diff --git a/package.json b/package.json index db4927d..eae1f28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "breezer.js", - "version": "0.4.9", + "version": "0.5.0", "description": "", "main": "dist/index.js", "scripts": { diff --git a/src/helpers/command.ts b/src/helpers/command.ts index bf06b64..5ef4f4a 100644 --- a/src/helpers/command.ts +++ b/src/helpers/command.ts @@ -10,7 +10,7 @@ const regex = { stateExp: /\$[a-zA-Z0-9-]+\$/g } -export class Command { +export class Command { structure:CmdStructure[]; name?:string; strict:boolean; @@ -100,13 +100,13 @@ export class Command { } } - const extracted:(string|number)[] = []; + const extracted = [] as unknown as Structure; for (let field in fields) { try { if (+field == this.structure.length - 1 && +field !== fields.length - 1) { if (this.structure[field].includes('string')) { let value = fields.splice(+field).join(' '); - extracted.push(value); + extracted.push(value as never); return extracted; } } @@ -115,11 +115,11 @@ export class Command { this.structure[field].split('|')[0] as 'number'|'string' ](fields[field], this.strict, this.name); - extracted.push(data); + extracted.push(data as never); } catch (e) { if (this.strict) { throw Error(e as string); - } else extracted.push(fields[field]); + } else extracted.push(fields[field] as never); } } return extracted; @@ -171,7 +171,7 @@ export class Command { } } -export class TypicalCommand extends Command { +export class TypicalCommand extends Command { constructor() { super({ structure: [], diff --git a/src/helpers/funcs.ts b/src/helpers/funcs.ts index 187fbe5..a43afc8 100644 --- a/src/helpers/funcs.ts +++ b/src/helpers/funcs.ts @@ -1,4 +1,4 @@ -import { Message, PermissionResolvable, TextChannel } from "discord.js"; +import { Intents, Message, PermissionResolvable, TextChannel } from "discord.js"; export function err(desc:string, cmd?:string, warn=false) { return `[${warn ? 'warn' : 'err'}][cmd: ${cmd}] ${desc}` @@ -35,4 +35,15 @@ export async function userHasPerm(perm:PermissionResolvable, userId:string, msg: const user = await msg.guild?.members.fetch(userId); if (!user) return false; return channel.permissionsFor(user).has(perm); +} + +export function getIntents() { + return [ + Intents.FLAGS.GUILD_MESSAGES, + Intents.FLAGS.GUILD_MESSAGE_REACTIONS, + Intents.FLAGS.MESSAGE_CONTENT, + Intents.FLAGS.GUILDS, + Intents.FLAGS.GUILD_MEMBERS, + Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS + ] } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 3cd693b..6272bd0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,29 +1,22 @@ -import discord, { Intents, Message, PermissionResolvable, TextChannel } from 'discord.js'; +import discord from 'discord.js'; import fs from 'fs'; import { TypicalCommand } from './helpers/command.js'; import { revealNameOfCmd } from './helpers/handlers.js'; import { Command } from './helpers/command.js'; import { StateManager } from './helpers/stateManager.js'; -import { buttonSignal, userHasPerm } from './helpers/funcs.js'; +import { buttonSignal, getIntents, userHasPerm } from './helpers/funcs.js'; class Bot { commands:string[]; bot:discord.Client; prefix:string; - private commandObjects:{ [index: string]: TypicalCommand }; + private commandObjects:{ [index: string]: TypicalCommand<[]> }; private token:string; private cmdFolder:string; private lang:string constructor(options:{ commandsFolder:string, token:string, prefix:string, lang:'.js'|'.ts' }) { - let intents = [ - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILD_MESSAGE_REACTIONS, - Intents.FLAGS.MESSAGE_CONTENT, - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MEMBERS, - Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS - ] + let intents = getIntents(); this.commands = fs.readdirSync(options.commandsFolder).map(i => i.replace(options.lang, '')); this.token = options.token; @@ -33,9 +26,10 @@ class Bot { this.bot = new discord.Client({ intents }); - let cmdsCollectd:{ [index: string]: TypicalCommand } = {}; + let cmdsCollectd:{ [index: string]: TypicalCommand<[]> } = {}; for (let command of this.commands) { - import(`file://${process.cwd()}/${this.cmdFolder}/${command}${this.lang}`).then(cmd => { + const cmdPath = `file://${process.cwd()}/${this.cmdFolder}/${command}${this.lang}`; + import(cmdPath).then(cmd => { if (cmd.default) cmdsCollectd[command] = cmd.default; }).catch(e => console.log(e)); @@ -48,9 +42,11 @@ class Bot { this.bot.on('messageCreate', async msg => { msg.content = msg.content.toLowerCase(); const cmdName = revealNameOfCmd(msg.content, this.prefix); + if (!cmdName || !this.commands.includes(cmdName)) return; let cmdClass = this.commandObjects[cmdName] as any; - const cmd = new cmdClass() as TypicalCommand + const cmd = new cmdClass(); + cmd.content = msg.content.replace(this.prefix, '').replace(/[ ]+/g, ' ').trim(); cmd.msg = msg; cmd.execute();