feat(extensions): the beginning
This commit is contained in:
		
							parent
							
								
									9c9726259b
								
							
						
					
					
						commit
						f6393c8226
					
				
					 5 changed files with 154 additions and 25 deletions
				
			
		|  | @ -1,12 +1,14 @@ | ||||||
| import { Gateway, GatewayEventHandler } from '../index.ts' | import { Gateway, GatewayEventHandler } from '../index.ts' | ||||||
| import getChannelByType from '../../utils/getChannelByType.ts' | import getChannelByType from '../../utils/getChannelByType.ts' | ||||||
| import { ChannelPayload } from '../../types/channel.ts' | import { ChannelPayload } from '../../types/channel.ts' | ||||||
|  | import { Guild } from "../../structures/guild.ts" | ||||||
| 
 | 
 | ||||||
| export const channelCreate: GatewayEventHandler = async ( | export const channelCreate: GatewayEventHandler = async ( | ||||||
|   gateway: Gateway, |   gateway: Gateway, | ||||||
|   d: ChannelPayload |   d: ChannelPayload | ||||||
| ) => { | ) => { | ||||||
|   const channel = getChannelByType(gateway.client, d) |   const guild: undefined | Guild = (d as any).guild_id !== undefined ? await gateway.client.guilds.get((d as any).guild_id) : undefined | ||||||
|  |   const channel = getChannelByType(gateway.client, d, guild) | ||||||
|   if (channel !== undefined) { |   if (channel !== undefined) { | ||||||
|     await gateway.client.channels.set(d.id, d) |     await gateway.client.channels.set(d.id, d) | ||||||
|     gateway.client.emit('channelCreate', channel) |     gateway.client.emit('channelCreate', channel) | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import { TextChannel } from '../structures/textChannel.ts' | ||||||
| import { User } from '../structures/user.ts' | import { User } from '../structures/user.ts' | ||||||
| import { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| import { CommandClient } from './commandClient.ts' | import { CommandClient } from './commandClient.ts' | ||||||
|  | import { Extension } from "./extensions.ts" | ||||||
| 
 | 
 | ||||||
| export interface CommandContext { | export interface CommandContext { | ||||||
|   /** The Client object */ |   /** The Client object */ | ||||||
|  | @ -35,8 +36,8 @@ export class Command { | ||||||
|   description?: string |   description?: string | ||||||
|   /** Array of Aliases of Command, or only string */ |   /** Array of Aliases of Command, or only string */ | ||||||
|   aliases?: string | string[] |   aliases?: string | string[] | ||||||
|   /** Category of the Command */ |   /** Extension (Parent) of the Command */ | ||||||
|   category?: string |   extension?: Extension | ||||||
|   /** Usage of Command, only Argument Names */ |   /** Usage of Command, only Argument Names */ | ||||||
|   usage?: string | string[] |   usage?: string | string[] | ||||||
|   /** Usage Example of Command, only Arguments (without Prefix and Name) */ |   /** Usage Example of Command, only Arguments (without Prefix and Name) */ | ||||||
|  | @ -58,13 +59,16 @@ export class Command { | ||||||
|   execute(ctx: CommandContext): any { } |   execute(ctx: CommandContext): any { } | ||||||
|   /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ |   /** Method executed after executing command, passes on CommandContext and the value returned by execute too. (optional) */ | ||||||
|   afterExecute(ctx: CommandContext, executeResult: any): any { } |   afterExecute(ctx: CommandContext, executeResult: any): any { } | ||||||
|  | 
 | ||||||
|  |   toString(): string { | ||||||
|  |     return `Command: ${this.name}${this.extension !== undefined ? ` [${this.extension.name}]` : ''}` | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class CommandsManager { | export class CommandsManager { | ||||||
|   client: CommandClient |   client: CommandClient | ||||||
|   list: Collection<string, Command> = new Collection() |   list: Collection<string, Command> = new Collection() | ||||||
|   disabled: Set<string> = new Set() |   disabled: Set<string> = new Set() | ||||||
|   disabledCategories: Set<string> = new Set() |  | ||||||
| 
 | 
 | ||||||
|   constructor(client: CommandClient) { |   constructor(client: CommandClient) { | ||||||
|     this.client = client |     this.client = client | ||||||
|  | @ -96,7 +100,6 @@ export class CommandsManager { | ||||||
|     const cmd = this.find(name) |     const cmd = this.find(name) | ||||||
|     if (cmd === undefined) return |     if (cmd === undefined) return | ||||||
|     if (this.isDisabled(cmd) && bypassDisable !== true) return |     if (this.isDisabled(cmd) && bypassDisable !== true) return | ||||||
|     if (cmd.category !== undefined && this.isCategoryDisabled(cmd.category) && bypassDisable !== true) return |  | ||||||
|     return cmd |     return cmd | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -121,7 +124,7 @@ export class CommandsManager { | ||||||
|   add(cmd: Command | typeof Command): boolean { |   add(cmd: Command | typeof Command): boolean { | ||||||
|     // eslint-disable-next-line new-cap
 |     // eslint-disable-next-line new-cap
 | ||||||
|     if (!(cmd instanceof Command)) cmd = new cmd() |     if (!(cmd instanceof Command)) cmd = new cmd() | ||||||
|     if (this.exists(cmd)) return false |     if (this.exists(cmd)) throw new Error(`Failed to add Command '${cmd.toString()}' with name/alias already exists.`) | ||||||
|     this.list.set(cmd.name, cmd) |     this.list.set(cmd.name, cmd) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
|  | @ -133,11 +136,6 @@ export class CommandsManager { | ||||||
|     else return this.list.delete(find.name) |     else return this.list.delete(find.name) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Get all Commands of given Category */ |  | ||||||
|   category(name: string): Collection<string, Command> { |  | ||||||
|     return this.list.filter(c => c.category === name) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** Check whether a Command is disabled or not */ |   /** Check whether a Command is disabled or not */ | ||||||
|   isDisabled(name: string | Command): boolean { |   isDisabled(name: string | Command): boolean { | ||||||
|     const cmd = typeof name === "string" ? this.find(name) : name |     const cmd = typeof name === "string" ? this.find(name) : name | ||||||
|  | @ -155,18 +153,6 @@ export class CommandsManager { | ||||||
|     this.disabled.add(cmd.name) |     this.disabled.add(cmd.name) | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   /** Check whether a Category is disabled */ |  | ||||||
|   isCategoryDisabled(name: string): boolean { |  | ||||||
|     return this.disabledCategories.has(name) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** Disable a Category of Commands */ |  | ||||||
|   disableCategory(name: string): boolean { |  | ||||||
|     if (this.isCategoryDisabled(name)) return false |  | ||||||
|     this.disabledCategories.add(name) |  | ||||||
|     return true |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface ParsedCommand { | export interface ParsedCommand { | ||||||
|  |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| import { Embed, Message } from '../../mod.ts' | import { Message } from "../../mod.ts" | ||||||
|  | import { Embed } from "../structures/embed.ts" | ||||||
| import { awaitSync } from "../utils/mixedPromise.ts" | import { awaitSync } from "../utils/mixedPromise.ts" | ||||||
| import { Client, ClientOptions } from './client.ts' | import { Client, ClientOptions } from './client.ts' | ||||||
| import { CommandContext, CommandsManager, parseCommand } from './command.ts' | import { CommandContext, CommandsManager, parseCommand } from './command.ts' | ||||||
|  | import { ExtensionsManager } from "./extensions.ts" | ||||||
| 
 | 
 | ||||||
| type PrefixReturnType = string | string[] | Promise<string | string[]> | type PrefixReturnType = string | string[] | Promise<string | string[]> | ||||||
| 
 | 
 | ||||||
|  | @ -62,6 +64,7 @@ export class CommandClient extends Client implements CommandClientOptions { | ||||||
|   allowBots: boolean |   allowBots: boolean | ||||||
|   allowDMs: boolean |   allowDMs: boolean | ||||||
|   caseSensitive: boolean |   caseSensitive: boolean | ||||||
|  |   extensions: ExtensionsManager = new ExtensionsManager(this) | ||||||
|   commands: CommandsManager = new CommandsManager(this) |   commands: CommandsManager = new CommandsManager(this) | ||||||
|   texts: CommandTexts = DefaultCommandTexts |   texts: CommandTexts = DefaultCommandTexts | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										112
									
								
								src/models/extensions.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/models/extensions.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | ||||||
|  | import { Collection } from "../utils/collection.ts"; | ||||||
|  | import { Command } from "./command.ts"; | ||||||
|  | import { CommandClient } from "./commandClient.ts"; | ||||||
|  | 
 | ||||||
|  | export type ExtensionEventCallback = (ext: Extension, ...args: any[]) => any | ||||||
|  | 
 | ||||||
|  | export class ExtensionCommands { | ||||||
|  |   extension: Extension | ||||||
|  | 
 | ||||||
|  |   constructor(ext: Extension) { | ||||||
|  |     this.extension = ext | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get list(): Collection<string, Command> { | ||||||
|  |     return this.extension.client.commands.list.filter(c => c.extension?.name === this.extension.name) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get(cmd: string): Command | undefined { | ||||||
|  |     const find = this.extension.client.commands.find(cmd) | ||||||
|  |     // linter sucks
 | ||||||
|  |     if (find === undefined) return undefined | ||||||
|  |     else if (find.extension === undefined) return undefined | ||||||
|  |     else if (find.extension.name !== this.extension.name) return undefined | ||||||
|  |     else return find | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   add(Cmd: Command | typeof Command): boolean { | ||||||
|  |     const cmd = Cmd instanceof Command ? Cmd : new Cmd() | ||||||
|  |     cmd.extension = this.extension | ||||||
|  |     return this.extension.client.commands.add(cmd) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   delete(cmd: Command | string): boolean { | ||||||
|  |     const find = this.extension.client.commands.find(typeof cmd === 'string' ? cmd : cmd.name) | ||||||
|  |     if (find === undefined) return false | ||||||
|  |     if (find.extension !== undefined && find.extension.name !== this.extension.name) return false | ||||||
|  |     else return this.extension.client.commands.delete(find) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   deleteAll(): void { | ||||||
|  |     for (const [cmd] of this.list) { | ||||||
|  |       this.delete(cmd) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class Extension { | ||||||
|  |   client: CommandClient | ||||||
|  |   name: string = '' | ||||||
|  |   description?: string | ||||||
|  |   commands: ExtensionCommands = new ExtensionCommands(this) | ||||||
|  |   events: { [name: string]: (...args: any[]) => {} } = {} | ||||||
|  | 
 | ||||||
|  |   constructor(client: CommandClient) { | ||||||
|  |     this.client = client | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   listen(event: string, cb: ExtensionEventCallback): boolean { | ||||||
|  |     if (this.events[event] !== undefined) return false | ||||||
|  |     else { | ||||||
|  |       const fn = (...args: any[]): any => { | ||||||
|  |         // eslint-disable-next-line standard/no-callback-literal
 | ||||||
|  |         cb(this, ...args) | ||||||
|  |       }; | ||||||
|  |       this.client.on(event, fn) | ||||||
|  |       this.events[event] = fn | ||||||
|  |       return true | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   load(): any {} | ||||||
|  |   unload(): any {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class ExtensionsManager { | ||||||
|  |   client: CommandClient | ||||||
|  |   list: Collection<string, Extension> = new Collection() | ||||||
|  | 
 | ||||||
|  |   constructor(client: CommandClient) { | ||||||
|  |     this.client = client | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get(ext: string): Extension | undefined { | ||||||
|  |     return this.list.get(ext) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   exists(ext: string): boolean { | ||||||
|  |     return this.get(ext) !== undefined | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   load(ext: Extension | typeof Extension): void { | ||||||
|  |     // eslint-disable-next-line new-cap
 | ||||||
|  |     if (!(ext instanceof Extension)) ext = new ext(this.client) | ||||||
|  |     if (this.exists(ext.name)) throw new Error(`Extension with name '${ext.name}' already exists`) | ||||||
|  |     this.list.set(ext.name, ext) | ||||||
|  |     ext.load() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   unload(ext: Extension | string): boolean { | ||||||
|  |     const name = typeof ext === 'string' ? ext : ext.name | ||||||
|  |     const extension = this.get(name) | ||||||
|  |     if (extension === undefined) return false | ||||||
|  |     extension.commands.deleteAll() | ||||||
|  |     for (const [k, v] of Object.entries(extension.events)) { | ||||||
|  |       this.client.removeListener(k, v) | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
 | ||||||
|  |       delete extension.events[k] | ||||||
|  |     } | ||||||
|  |     extension.unload() | ||||||
|  |     return this.list.delete(name) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1,4 +1,7 @@ | ||||||
| import { CommandClient, Intents } from '../../mod.ts' | import { Command, CommandClient, Intents } from '../../mod.ts' | ||||||
|  | import { GuildChannel } from "../managers/guildChannels.ts" | ||||||
|  | import { CommandContext } from "../models/command.ts" | ||||||
|  | import { Extension } from "../models/extensions.ts" | ||||||
| import { TOKEN } from './config.ts' | import { TOKEN } from './config.ts' | ||||||
| 
 | 
 | ||||||
| const client = new CommandClient({ | const client = new CommandClient({ | ||||||
|  | @ -17,6 +20,29 @@ client.on('ready', () => { | ||||||
| 
 | 
 | ||||||
| client.on("commandError", console.error) | client.on("commandError", console.error) | ||||||
| 
 | 
 | ||||||
|  | class ChannelLog extends Extension { | ||||||
|  | 
 | ||||||
|  |   onChannelCreate(ext: Extension, channel: GuildChannel): void { | ||||||
|  |     console.log(`Channel Created: ${channel.name}`) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   load(): void { | ||||||
|  |     this.listen('channelCreate', this.onChannelCreate) | ||||||
|  | 
 | ||||||
|  |     class Pong extends Command { | ||||||
|  |       name = 'Pong' | ||||||
|  | 
 | ||||||
|  |       execute(ctx: CommandContext): any { | ||||||
|  |         ctx.message.reply('Ping!') | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.commands.add(Pong) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | client.extensions.load(ChannelLog) | ||||||
|  | 
 | ||||||
| // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
| ;(async() => { | ;(async() => { | ||||||
|   const files = Deno.readDirSync('./src/test/cmds') |   const files = Deno.readDirSync('./src/test/cmds') | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue