From e1281736ec577b4bba2091a029148ab7a1d22e4b Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 10 Dec 2020 14:40:00 +0530 Subject: [PATCH] feat(slash): add @slash decorator support --- mod.ts | 3 ++ src/models/client.ts | 23 ++++++++- src/models/slashClient.ts | 102 ++++++++++++++++++++++++++++++++------ src/test/slash.ts | 58 ++++++++-------------- 4 files changed, 134 insertions(+), 52 deletions(-) diff --git a/mod.ts b/mod.ts index 9606984..40c4ccb 100644 --- a/mod.ts +++ b/mod.ts @@ -4,6 +4,7 @@ export { Base } from './src/structures/base.ts' export { Gateway } from './src/gateway/index.ts' export type { ClientEvents } from './src/gateway/handlers/index.ts' export * from './src/models/client.ts' +export * from './src/models/slashClient.ts' export { RESTManager } from './src/models/rest.ts' export * from './src/models/cacheAdapter.ts' export { @@ -29,6 +30,8 @@ export { GatewayCache } from './src/managers/gatewayCache.ts' export { GuildChannelsManager } from './src/managers/guildChannels.ts' export type { GuildChannel } from './src/managers/guildChannels.ts' export { GuildManager } from './src/managers/guilds.ts' +export * from './src/structures/slash.ts' +export * from './src/types/slash.ts' export { GuildEmojisManager } from './src/managers/guildEmojis.ts' export { MembersManager } from './src/managers/members.ts' export { MessageReactionsManager } from './src/managers/messageReactions.ts' diff --git a/src/models/client.ts b/src/models/client.ts index 033cc1b..98dda1d 100644 --- a/src/models/client.ts +++ b/src/models/client.ts @@ -13,6 +13,7 @@ import { ActivityGame, ClientActivity } from '../types/presence.ts' import { ClientEvents } from '../gateway/handlers/index.ts' import { Extension } from './extensions.ts' import { SlashClient } from './slashClient.ts' +import { Interaction } from '../structures/slash.ts' /** OS related properties sent with Gateway Identify */ export interface ClientProperties { @@ -43,6 +44,8 @@ export interface ClientOptions { fetchUncachedReactions?: boolean /** Client Properties */ clientProperties?: ClientProperties + /** Enable/Disable Slash Commands Integration (enabled by default) */ + enableSlash?: boolean } /** @@ -86,6 +89,11 @@ export class Client extends EventEmitter { /** Client's presence. Startup one if set before connecting */ presence: ClientPresence = new ClientPresence() _decoratedEvents?: { [name: string]: (...args: any[]) => any } + _decoratedSlash?: Array<{ + name: string + guild?: string + handler: (interaction: Interaction) => any + }> private readonly _untypedOn = this.on @@ -137,7 +145,9 @@ export class Client extends EventEmitter { } : options.clientProperties - this.slash = new SlashClient(this) + this.slash = new SlashClient(this, { + enabled: options.enableSlash + }) } /** @@ -200,3 +210,14 @@ export function event(name?: string) { client._decoratedEvents[name === undefined ? prop : name] = listener } } + +export function slash(name?: string, guild?: string) { + return function (client: Client, prop: string) { + if (client._decoratedSlash === undefined) client._decoratedSlash = [] + client._decoratedSlash.push({ + name: name ?? prop, + guild, + handler: (client as { [name: string]: any })[prop] + }) + } +} diff --git a/src/models/slashClient.ts b/src/models/slashClient.ts index b6ce775..b008b56 100644 --- a/src/models/slashClient.ts +++ b/src/models/slashClient.ts @@ -19,22 +19,33 @@ export interface SlashOptions { } export class SlashCommand { + slash: SlashCommandsManager id: string applicationID: string name: string description: string options: SlashCommandOption[] + _guild?: string - constructor(data: SlashCommandPayload) { + constructor(manager: SlashCommandsManager, data: SlashCommandPayload) { + this.slash = manager this.id = data.id this.applicationID = data.application_id this.name = data.name this.description = data.description this.options = data.options } + + async delete(): Promise { + await this.slash.delete(this.id, this._guild) + } + + async edit(data: SlashCommandPartial): Promise { + await this.slash.edit(this.id, data, this._guild) + } } -export class SlashCommands { +export class SlashCommandsManager { client: Client slash: SlashClient @@ -53,7 +64,8 @@ export class SlashCommands { if (!Array.isArray(res)) return col for (const raw of res) { - col.set(raw.id, new SlashCommand(raw)) + const cmd = new SlashCommand(this, raw) + col.set(raw.id, cmd) } return col @@ -74,7 +86,9 @@ export class SlashCommands { if (!Array.isArray(res)) return col for (const raw of res) { - col.set(raw.id, new SlashCommand(raw)) + const cmd = new SlashCommand(this, raw) + cmd._guild = typeof guild === 'string' ? guild : guild.id + col.set(raw.id, cmd) } return col @@ -95,14 +109,19 @@ export class SlashCommands { data ) - return new SlashCommand(payload) + const cmd = new SlashCommand(this, payload) + cmd._guild = + typeof guild === 'string' || guild === undefined ? guild : guild.id + + return cmd } + /** Edit a Slash Command (global or Guild) */ async edit( id: string, - data: SlashCommandPayload, - guild?: Guild - ): Promise { + data: SlashCommandPartial, + guild?: Guild | string + ): Promise { await this.client.rest.patch( guild === undefined ? APPLICATION_COMMAND(this.client.user?.id as string, id) @@ -115,30 +134,85 @@ export class SlashCommands { ) return this } + + /** Delete a Slash Command (global or Guild) */ + async delete( + id: string, + guild?: Guild | string + ): Promise { + await this.client.rest.delete( + guild === undefined + ? APPLICATION_COMMAND(this.client.user?.id as string, id) + : APPLICATION_GUILD_COMMAND( + this.client.user?.id as string, + typeof guild === 'string' ? guild : guild.id, + id + ) + ) + return this + } +} + +export type SlashCommandHandlerCallback = (interaction: Interaction) => any +export interface SlashCommandHandler { + name: string + guild?: string + handler: SlashCommandHandlerCallback } export class SlashClient { client: Client enabled: boolean = true - commands: SlashCommands + commands: SlashCommandsManager + handlers: SlashCommandHandler[] = [] constructor(client: Client, options?: SlashOptions) { this.client = client - this.commands = new SlashCommands(client) + this.commands = new SlashCommandsManager(client) if (options !== undefined) { this.enabled = options.enabled ?? true } + if (this.client._decoratedSlash !== undefined) { + this.client._decoratedSlash.forEach((e) => { + this.handlers.push(e) + }) + } + this.client.on('interactionCreate', (interaction) => this.process(interaction) ) } - process(interaction: Interaction): any {} - - handle(fn: (interaction: Interaction) => any): SlashClient { - this.process = fn + /** Adds a new Slash Command Handler */ + handle( + name: string, + handler: SlashCommandHandlerCallback, + guild?: string + ): SlashClient { + this.handlers.push({ + name, + guild, + handler + }) return this } + + process(interaction: Interaction): any { + if (!this.enabled) return + + let cmd + + if (interaction.guild !== undefined) + cmd = + this.handlers.find( + (e) => e.guild !== undefined && e.name === interaction.name + ) ?? this.handlers.find((e) => e.name === interaction.name) + else cmd = this.handlers.find((e) => e.name === interaction.name) + + if (cmd === undefined) return + + cmd.handler(interaction) + } } diff --git a/src/test/slash.ts b/src/test/slash.ts index b4e6192..688a855 100644 --- a/src/test/slash.ts +++ b/src/test/slash.ts @@ -1,33 +1,23 @@ -import { Client, Intents } from '../../mod.ts' +import { Client, Intents, event, slash } from '../../mod.ts' import { Embed } from '../structures/embed.ts' -import { SlashCommandOptionType } from '../types/slash.ts' +import { Interaction } from '../structures/slash.ts' import { TOKEN } from './config.ts' -const client = new Client() +export class MyClient extends Client { + @event() + ready(): void { + console.log(`Logged in as ${this.user?.tag}!`) + } -client.on('ready', () => { - console.log('Logged in!') - client.slash.commands - .create( - { - name: 'send', - description: 'Send a Message through Bot!', - options: [ - { - name: 'content', - description: 'Message to send', - type: SlashCommandOptionType.STRING, - required: true - } - ] - }, - '783319033205751809' - ) - .then(console.log) -}) + @slash() + send(d: Interaction): void { + d.respond({ + content: d.data.options.find((e) => e.name === 'content')?.value + }) + } -client.on('interactionCreate', async (d) => { - if (d.name === 'eval') { + @slash() + async eval(d: Interaction): Promise { if (d.user.id !== '422957901716652033') { d.respond({ content: 'This command can only be used by owner!' @@ -53,8 +43,10 @@ client.on('interactionCreate', async (d) => { }) } } - return - } else if (d.name === 'hug') { + } + + @slash() + async hug(d: Interaction): Promise { const id = d.data.options.find((e) => e.name === 'user')?.value as string const user = (await client.users.get(id)) ?? (await client.users.fetch(id)) const url = await fetch('https://nekos.life/api/v2/img/hug') @@ -69,16 +61,8 @@ client.on('interactionCreate', async (d) => { .setColor(0x2f3136) ] }) - return - } else if (d.name === 'send') { - d.respond({ - content: d.data.options.find((e) => e.name === 'content')?.value as string - }) - return } - await d.respond({ - content: `Hi, ${d.member.user.username}! You used /${d.name}` - }) -}) +} +const client = new MyClient() client.connect(TOKEN, Intents.None)