feat(slash): add @slash decorator support
This commit is contained in:
		
							parent
							
								
									b0d6092c25
								
							
						
					
					
						commit
						e1281736ec
					
				
					 4 changed files with 134 additions and 52 deletions
				
			
		
							
								
								
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							|  | @ -4,6 +4,7 @@ export { Base } from './src/structures/base.ts' | ||||||
| export { Gateway } from './src/gateway/index.ts' | export { Gateway } from './src/gateway/index.ts' | ||||||
| export type { ClientEvents } from './src/gateway/handlers/index.ts' | export type { ClientEvents } from './src/gateway/handlers/index.ts' | ||||||
| export * from './src/models/client.ts' | export * from './src/models/client.ts' | ||||||
|  | export * from './src/models/slashClient.ts' | ||||||
| export { RESTManager } from './src/models/rest.ts' | export { RESTManager } from './src/models/rest.ts' | ||||||
| export * from './src/models/cacheAdapter.ts' | export * from './src/models/cacheAdapter.ts' | ||||||
| export { | export { | ||||||
|  | @ -29,6 +30,8 @@ export { GatewayCache } from './src/managers/gatewayCache.ts' | ||||||
| export { GuildChannelsManager } from './src/managers/guildChannels.ts' | export { GuildChannelsManager } from './src/managers/guildChannels.ts' | ||||||
| export type { GuildChannel } from './src/managers/guildChannels.ts' | export type { GuildChannel } from './src/managers/guildChannels.ts' | ||||||
| export { GuildManager } from './src/managers/guilds.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 { GuildEmojisManager } from './src/managers/guildEmojis.ts' | ||||||
| export { MembersManager } from './src/managers/members.ts' | export { MembersManager } from './src/managers/members.ts' | ||||||
| export { MessageReactionsManager } from './src/managers/messageReactions.ts' | export { MessageReactionsManager } from './src/managers/messageReactions.ts' | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ import { ActivityGame, ClientActivity } from '../types/presence.ts' | ||||||
| import { ClientEvents } from '../gateway/handlers/index.ts' | import { ClientEvents } from '../gateway/handlers/index.ts' | ||||||
| import { Extension } from './extensions.ts' | import { Extension } from './extensions.ts' | ||||||
| import { SlashClient } from './slashClient.ts' | import { SlashClient } from './slashClient.ts' | ||||||
|  | import { Interaction } from '../structures/slash.ts' | ||||||
| 
 | 
 | ||||||
| /** OS related properties sent with Gateway Identify */ | /** OS related properties sent with Gateway Identify */ | ||||||
| export interface ClientProperties { | export interface ClientProperties { | ||||||
|  | @ -43,6 +44,8 @@ export interface ClientOptions { | ||||||
|   fetchUncachedReactions?: boolean |   fetchUncachedReactions?: boolean | ||||||
|   /** Client Properties */ |   /** Client Properties */ | ||||||
|   clientProperties?: ClientProperties |   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 */ |   /** Client's presence. Startup one if set before connecting */ | ||||||
|   presence: ClientPresence = new ClientPresence() |   presence: ClientPresence = new ClientPresence() | ||||||
|   _decoratedEvents?: { [name: string]: (...args: any[]) => any } |   _decoratedEvents?: { [name: string]: (...args: any[]) => any } | ||||||
|  |   _decoratedSlash?: Array<{ | ||||||
|  |     name: string | ||||||
|  |     guild?: string | ||||||
|  |     handler: (interaction: Interaction) => any | ||||||
|  |   }> | ||||||
| 
 | 
 | ||||||
|   private readonly _untypedOn = this.on |   private readonly _untypedOn = this.on | ||||||
| 
 | 
 | ||||||
|  | @ -137,7 +145,9 @@ export class Client extends EventEmitter { | ||||||
|           } |           } | ||||||
|         : options.clientProperties |         : 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 |     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] | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -19,22 +19,33 @@ export interface SlashOptions { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class SlashCommand { | export class SlashCommand { | ||||||
|  |   slash: SlashCommandsManager | ||||||
|   id: string |   id: string | ||||||
|   applicationID: string |   applicationID: string | ||||||
|   name: string |   name: string | ||||||
|   description: string |   description: string | ||||||
|   options: SlashCommandOption[] |   options: SlashCommandOption[] | ||||||
|  |   _guild?: string | ||||||
| 
 | 
 | ||||||
|   constructor(data: SlashCommandPayload) { |   constructor(manager: SlashCommandsManager, data: SlashCommandPayload) { | ||||||
|  |     this.slash = manager | ||||||
|     this.id = data.id |     this.id = data.id | ||||||
|     this.applicationID = data.application_id |     this.applicationID = data.application_id | ||||||
|     this.name = data.name |     this.name = data.name | ||||||
|     this.description = data.description |     this.description = data.description | ||||||
|     this.options = data.options |     this.options = data.options | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async delete(): Promise<void> { | ||||||
|  |     await this.slash.delete(this.id, this._guild) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async edit(data: SlashCommandPartial): Promise<void> { | ||||||
|  |     await this.slash.edit(this.id, data, this._guild) | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class SlashCommands { | export class SlashCommandsManager { | ||||||
|   client: Client |   client: Client | ||||||
|   slash: SlashClient |   slash: SlashClient | ||||||
| 
 | 
 | ||||||
|  | @ -53,7 +64,8 @@ export class SlashCommands { | ||||||
|     if (!Array.isArray(res)) return col |     if (!Array.isArray(res)) return col | ||||||
| 
 | 
 | ||||||
|     for (const raw of res) { |     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 |     return col | ||||||
|  | @ -74,7 +86,9 @@ export class SlashCommands { | ||||||
|     if (!Array.isArray(res)) return col |     if (!Array.isArray(res)) return col | ||||||
| 
 | 
 | ||||||
|     for (const raw of res) { |     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 |     return col | ||||||
|  | @ -95,14 +109,19 @@ export class SlashCommands { | ||||||
|       data |       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( |   async edit( | ||||||
|     id: string, |     id: string, | ||||||
|     data: SlashCommandPayload, |     data: SlashCommandPartial, | ||||||
|     guild?: Guild |     guild?: Guild | string | ||||||
|   ): Promise<SlashCommands> { |   ): Promise<SlashCommandsManager> { | ||||||
|     await this.client.rest.patch( |     await this.client.rest.patch( | ||||||
|       guild === undefined |       guild === undefined | ||||||
|         ? APPLICATION_COMMAND(this.client.user?.id as string, id) |         ? APPLICATION_COMMAND(this.client.user?.id as string, id) | ||||||
|  | @ -115,30 +134,85 @@ export class SlashCommands { | ||||||
|     ) |     ) | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /** Delete a Slash Command (global or Guild) */ | ||||||
|  |   async delete( | ||||||
|  |     id: string, | ||||||
|  |     guild?: Guild | string | ||||||
|  |   ): Promise<SlashCommandsManager> { | ||||||
|  |     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 { | export class SlashClient { | ||||||
|   client: Client |   client: Client | ||||||
|   enabled: boolean = true |   enabled: boolean = true | ||||||
|   commands: SlashCommands |   commands: SlashCommandsManager | ||||||
|  |   handlers: SlashCommandHandler[] = [] | ||||||
| 
 | 
 | ||||||
|   constructor(client: Client, options?: SlashOptions) { |   constructor(client: Client, options?: SlashOptions) { | ||||||
|     this.client = client |     this.client = client | ||||||
|     this.commands = new SlashCommands(client) |     this.commands = new SlashCommandsManager(client) | ||||||
| 
 | 
 | ||||||
|     if (options !== undefined) { |     if (options !== undefined) { | ||||||
|       this.enabled = options.enabled ?? true |       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.client.on('interactionCreate', (interaction) => | ||||||
|       this.process(interaction) |       this.process(interaction) | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   process(interaction: Interaction): any {} |   /** Adds a new Slash Command Handler */ | ||||||
| 
 |   handle( | ||||||
|   handle(fn: (interaction: Interaction) => any): SlashClient { |     name: string, | ||||||
|     this.process = fn |     handler: SlashCommandHandlerCallback, | ||||||
|  |     guild?: string | ||||||
|  |   ): SlashClient { | ||||||
|  |     this.handlers.push({ | ||||||
|  |       name, | ||||||
|  |       guild, | ||||||
|  |       handler | ||||||
|  |     }) | ||||||
|     return this |     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) | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 { Embed } from '../structures/embed.ts' | ||||||
| import { SlashCommandOptionType } from '../types/slash.ts' | import { Interaction } from '../structures/slash.ts' | ||||||
| import { TOKEN } from './config.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', () => { |   @slash() | ||||||
|   console.log('Logged in!') |   send(d: Interaction): void { | ||||||
|   client.slash.commands |     d.respond({ | ||||||
|     .create( |       content: d.data.options.find((e) => e.name === 'content')?.value | ||||||
|       { |     }) | ||||||
|         name: 'send', |   } | ||||||
|         description: 'Send a Message through Bot!', |  | ||||||
|         options: [ |  | ||||||
|           { |  | ||||||
|             name: 'content', |  | ||||||
|             description: 'Message to send', |  | ||||||
|             type: SlashCommandOptionType.STRING, |  | ||||||
|             required: true |  | ||||||
|           } |  | ||||||
|         ] |  | ||||||
|       }, |  | ||||||
|       '783319033205751809' |  | ||||||
|     ) |  | ||||||
|     .then(console.log) |  | ||||||
| }) |  | ||||||
| 
 | 
 | ||||||
| client.on('interactionCreate', async (d) => { |   @slash() | ||||||
|   if (d.name === 'eval') { |   async eval(d: Interaction): Promise<void> { | ||||||
|     if (d.user.id !== '422957901716652033') { |     if (d.user.id !== '422957901716652033') { | ||||||
|       d.respond({ |       d.respond({ | ||||||
|         content: 'This command can only be used by owner!' |         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<void> { | ||||||
|     const id = d.data.options.find((e) => e.name === 'user')?.value as string |     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 user = (await client.users.get(id)) ?? (await client.users.fetch(id)) | ||||||
|     const url = await fetch('https://nekos.life/api/v2/img/hug') |     const url = await fetch('https://nekos.life/api/v2/img/hug') | ||||||
|  | @ -69,16 +61,8 @@ client.on('interactionCreate', async (d) => { | ||||||
|           .setColor(0x2f3136) |           .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) | client.connect(TOKEN, Intents.None) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue