message component interactions
This commit is contained in:
		
							parent
							
								
									17e74dce45
								
							
						
					
					
						commit
						b97ec3c225
					
				
					 18 changed files with 461 additions and 86 deletions
				
			
		
							
								
								
									
										4
									
								
								mod.ts
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								mod.ts
									
										
									
									
									
								
							|  | @ -39,8 +39,11 @@ export { GuildChannelsManager } from './src/managers/guildChannels.ts' | |||
| export { GuildManager } from './src/managers/guilds.ts' | ||||
| export * from './src/structures/base.ts' | ||||
| export * from './src/structures/slash.ts' | ||||
| export * from './src/structures/interactions.ts' | ||||
| export * from './src/structures/messageComponents.ts' | ||||
| export * from './src/types/slashCommands.ts' | ||||
| export * from './src/types/interactions.ts' | ||||
| export * from './src/types/messageComponents.ts' | ||||
| export { GuildEmojisManager } from './src/managers/guildEmojis.ts' | ||||
| export { MembersManager } from './src/managers/members.ts' | ||||
| export { MessageReactionsManager } from './src/managers/messageReactions.ts' | ||||
|  | @ -192,3 +195,4 @@ export { | |||
|   isVoiceChannel, | ||||
|   default as getChannelByType | ||||
| } from './src/utils/channel.ts' | ||||
| export * from './src/utils/interactions.ts' | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ import { EmojisManager } from '../managers/emojis.ts' | |||
| import { ActivityGame, ClientActivity } from '../types/presence.ts' | ||||
| import type { Extension } from '../commands/extension.ts' | ||||
| import { SlashClient } from '../interactions/slashClient.ts' | ||||
| import type { Interaction } from '../structures/slash.ts' | ||||
| import { ShardManager } from './shard.ts' | ||||
| import { Application } from '../structures/application.ts' | ||||
| import { Invite } from '../structures/invite.ts' | ||||
|  | @ -113,17 +112,6 @@ export class Client extends HarmonyEventEmitter<ClientEvents> { | |||
| 
 | ||||
|   /** Client's presence. Startup one if set before connecting */ | ||||
|   presence: ClientPresence = new ClientPresence() | ||||
|   _decoratedEvents?: { | ||||
|     [name: string]: (...args: any[]) => void | ||||
|   } | ||||
| 
 | ||||
|   _decoratedSlash?: Array<{ | ||||
|     name: string | ||||
|     guild?: string | ||||
|     parent?: string | ||||
|     group?: string | ||||
|     handler: (interaction: Interaction) => any | ||||
|   }> | ||||
| 
 | ||||
|   _id?: string | ||||
| 
 | ||||
|  | @ -175,13 +163,13 @@ export class Client extends HarmonyEventEmitter<ClientEvents> { | |||
|       this.fetchUncachedReactions = true | ||||
| 
 | ||||
|     if ( | ||||
|       this._decoratedEvents !== undefined && | ||||
|       Object.keys(this._decoratedEvents).length !== 0 | ||||
|       (this as any)._decoratedEvents !== undefined && | ||||
|       Object.keys((this as any)._decoratedEvents).length !== 0 | ||||
|     ) { | ||||
|       Object.entries(this._decoratedEvents).forEach((entry) => { | ||||
|         this.on(entry[0] as keyof ClientEvents, entry[1].bind(this)) | ||||
|       Object.entries((this as any)._decoratedEvents).forEach((entry) => { | ||||
|         this.on(entry[0] as keyof ClientEvents, (entry as any)[1].bind(this)) | ||||
|       }) | ||||
|       this._decoratedEvents = undefined | ||||
|       ;(this as any)._decoratedEvents = undefined | ||||
|     } | ||||
| 
 | ||||
|     this.clientProperties = | ||||
|  | @ -422,19 +410,23 @@ export class Client extends HarmonyEventEmitter<ClientEvents> { | |||
| } | ||||
| 
 | ||||
| /** Event decorator to create an Event handler from function */ | ||||
| // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
 | ||||
| export function event(name?: keyof ClientEvents) { | ||||
|   return function ( | ||||
|     client: Client | Extension, | ||||
|     prop: keyof ClientEvents | string | ||||
|   ) { | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     const c = client as any | ||||
|     const listener = ((client as unknown) as { | ||||
|       [name in keyof ClientEvents]: (...args: ClientEvents[name]) => any | ||||
|     })[(prop as unknown) as keyof ClientEvents] | ||||
|     if (typeof listener !== 'function') | ||||
|       throw new Error('@event decorator requires a function') | ||||
|     if (client._decoratedEvents === undefined) client._decoratedEvents = {} | ||||
| 
 | ||||
|     if (c._decoratedEvents === undefined) c._decoratedEvents = {} | ||||
|     const key = name === undefined ? prop : name | ||||
| 
 | ||||
|     client._decoratedEvents[key] = listener | ||||
|     c._decoratedEvents[key] = listener | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -73,27 +73,25 @@ export class Extension { | |||
|   /** Events registered by this Extension */ | ||||
|   events: { [name: string]: (...args: any[]) => {} } = {} | ||||
| 
 | ||||
|   _decoratedCommands?: { [name: string]: Command } | ||||
|   _decoratedEvents?: { [name: string]: (...args: any[]) => any } | ||||
| 
 | ||||
|   constructor(client: CommandClient) { | ||||
|     this.client = client | ||||
|     if (this._decoratedCommands !== undefined) { | ||||
|       Object.entries(this._decoratedCommands).forEach((entry) => { | ||||
|     const self = this as any | ||||
|     if (self._decoratedCommands !== undefined) { | ||||
|       Object.entries(self._decoratedCommands).forEach((entry: any) => { | ||||
|         entry[1].extension = this | ||||
|         this.commands.add(entry[1]) | ||||
|       }) | ||||
|       this._decoratedCommands = undefined | ||||
|       self._decoratedCommands = undefined | ||||
|     } | ||||
| 
 | ||||
|     if ( | ||||
|       this._decoratedEvents !== undefined && | ||||
|       Object.keys(this._decoratedEvents).length !== 0 | ||||
|       self._decoratedEvents !== undefined && | ||||
|       Object.keys(self._decoratedEvents).length !== 0 | ||||
|     ) { | ||||
|       Object.entries(this._decoratedEvents).forEach((entry) => { | ||||
|       Object.entries(self._decoratedEvents).forEach((entry: any) => { | ||||
|         this.listen(entry[0] as keyof ClientEvents, entry[1].bind(this)) | ||||
|       }) | ||||
|       this._decoratedEvents = undefined | ||||
|       self._decoratedEvents = undefined | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,11 @@ import { Permissions } from '../../utils/permissions.ts' | |||
| import type { Gateway, GatewayEventHandler } from '../mod.ts' | ||||
| import { User } from '../../structures/user.ts' | ||||
| import { Role } from '../../structures/role.ts' | ||||
| import { RolePayload } from '../../types/role.ts' | ||||
| import { InteractionChannelPayload } from '../../types/slashCommands.ts' | ||||
| import { Message } from '../../structures/message.ts' | ||||
| import { TextChannel } from '../../structures/textChannel.ts' | ||||
| import { MessageComponentInteraction } from '../../structures/messageComponents.ts' | ||||
| 
 | ||||
| export const interactionCreate: GatewayEventHandler = async ( | ||||
|   gateway: Gateway, | ||||
|  | @ -32,13 +37,22 @@ export const interactionCreate: GatewayEventHandler = async ( | |||
|   const guild = | ||||
|     d.guild_id === undefined | ||||
|       ? undefined | ||||
|       : await gateway.client.guilds.get(d.guild_id) | ||||
|       : (await gateway.client.guilds.get(d.guild_id)) ?? | ||||
|         new Guild(gateway.client, { unavailable: true, id: d.guild_id } as any) | ||||
| 
 | ||||
|   if (d.member !== undefined) | ||||
|     await guild?.members.set(d.member.user.id, d.member) | ||||
|   const member = | ||||
|     d.member !== undefined | ||||
|       ? (((await guild?.members.get(d.member.user.id)) as unknown) as Member) | ||||
|       ? (await guild?.members.get(d.member.user.id))! ?? | ||||
|         new Member( | ||||
|           gateway.client, | ||||
|           d.member!, | ||||
|           new User(gateway.client, d.member.user), | ||||
|           // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|           guild!, | ||||
|           new Permissions(d.member.permissions) | ||||
|         ) | ||||
|       : undefined | ||||
|   if (d.user !== undefined) await gateway.client.users.set(d.user.id, d.user) | ||||
|   const dmUser = | ||||
|  | @ -47,9 +61,9 @@ export const interactionCreate: GatewayEventHandler = async ( | |||
|   const user = member !== undefined ? member.user : dmUser | ||||
|   if (user === undefined) return | ||||
| 
 | ||||
|   const channel = | ||||
|     (await gateway.client.channels.get<GuildTextBasedChannel>(d.channel_id)) ?? | ||||
|     (await gateway.client.channels.fetch<GuildTextBasedChannel>(d.channel_id)) | ||||
|   const channel = await gateway.client.channels.get<GuildTextBasedChannel>( | ||||
|     d.channel_id | ||||
|   ) | ||||
| 
 | ||||
|   const resolved: InteractionApplicationCommandResolved = { | ||||
|     users: {}, | ||||
|  | @ -58,9 +72,11 @@ export const interactionCreate: GatewayEventHandler = async ( | |||
|     roles: {} | ||||
|   } | ||||
| 
 | ||||
|   if (d.data?.resolved !== undefined) { | ||||
|     for (const [id, data] of Object.entries(d.data.resolved.users ?? {})) { | ||||
|       await gateway.client.users.set(id, data) | ||||
|   if ((d.data as any)?.resolved !== undefined) { | ||||
|     for (const [id, data] of Object.entries( | ||||
|       (d.data as any)?.resolved.users ?? {} | ||||
|     )) { | ||||
|       await gateway.client.users.set(id, data as UserPayload) | ||||
|       resolved.users[id] = ((await gateway.client.users.get( | ||||
|         id | ||||
|       )) as unknown) as User | ||||
|  | @ -68,43 +84,69 @@ export const interactionCreate: GatewayEventHandler = async ( | |||
|         resolved.users[id].member = resolved.members[id] | ||||
|     } | ||||
| 
 | ||||
|     for (const [id, data] of Object.entries(d.data.resolved.members ?? {})) { | ||||
|     for (const [id, data] of Object.entries( | ||||
|       (d.data as any)?.resolved.members ?? {} | ||||
|     )) { | ||||
|       const roles = await guild?.roles.array() | ||||
|       let permissions = new Permissions(Permissions.DEFAULT) | ||||
|       if (roles !== undefined) { | ||||
|         const mRoles = roles.filter( | ||||
|           (r) => (data?.roles?.includes(r.id) as boolean) || r.id === guild?.id | ||||
|           (r) => | ||||
|             ((data as any)?.roles?.includes(r.id) as boolean) || | ||||
|             r.id === guild?.id | ||||
|         ) | ||||
|         permissions = new Permissions(mRoles.map((r) => r.permissions)) | ||||
|       } | ||||
|       data.user = (d.data.resolved.users?.[id] as unknown) as UserPayload | ||||
|       ;(data as any).user = ((d.data as any).resolved.users?.[ | ||||
|         id | ||||
|       ] as unknown) as UserPayload | ||||
|       resolved.members[id] = new Member( | ||||
|         gateway.client, | ||||
|         data, | ||||
|         data as any, | ||||
|         resolved.users[id], | ||||
|         guild as Guild, | ||||
|         permissions | ||||
|       ) | ||||
|     } | ||||
| 
 | ||||
|     for (const [id, data] of Object.entries(d.data.resolved.roles ?? {})) { | ||||
|     for (const [id, data] of Object.entries( | ||||
|       (d.data as any).resolved.roles ?? {} | ||||
|     )) { | ||||
|       if (guild !== undefined) { | ||||
|         await guild.roles.set(id, data) | ||||
|         await guild.roles.set(id, data as RolePayload) | ||||
|         resolved.roles[id] = ((await guild.roles.get(id)) as unknown) as Role | ||||
|       } else { | ||||
|         resolved.roles[id] = new Role( | ||||
|           gateway.client, | ||||
|           data, | ||||
|           data as any, | ||||
|           (guild as unknown) as Guild | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     for (const [id, data] of Object.entries(d.data.resolved.channels ?? {})) { | ||||
|       resolved.channels[id] = new InteractionChannel(gateway.client, data) | ||||
|     for (const [id, data] of Object.entries( | ||||
|       (d.data as any).resolved.channels ?? {} | ||||
|     )) { | ||||
|       resolved.channels[id] = new InteractionChannel( | ||||
|         gateway.client, | ||||
|         data as InteractionChannelPayload | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   let message: Message | undefined | ||||
|   if (d.message !== undefined) { | ||||
|     const channel = (await gateway.client.channels.get<TextChannel>( | ||||
|       d.message.channel_id | ||||
|     ))! | ||||
|     message = new Message( | ||||
|       gateway.client, | ||||
|       d.message, | ||||
|       channel, | ||||
|       new User(gateway.client, d.message.author) | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   let interaction | ||||
|   if (d.type === InteractionType.APPLICATION_COMMAND) { | ||||
|     interaction = new SlashCommandInteraction(gateway.client, d, { | ||||
|  | @ -114,12 +156,21 @@ export const interactionCreate: GatewayEventHandler = async ( | |||
|       user, | ||||
|       resolved | ||||
|     }) | ||||
|   } else if (d.type === InteractionType.MESSAGE_COMPONENT) { | ||||
|     interaction = new MessageComponentInteraction(gateway.client, d, { | ||||
|       member, | ||||
|       guild, | ||||
|       channel, | ||||
|       user, | ||||
|       message | ||||
|     }) | ||||
|   } else { | ||||
|     interaction = new Interaction(gateway.client, d, { | ||||
|       member, | ||||
|       guild, | ||||
|       channel, | ||||
|       user | ||||
|       user, | ||||
|       message | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,6 +69,7 @@ import { applicationCommandCreate } from './applicationCommandCreate.ts' | |||
| import { applicationCommandDelete } from './applicationCommandDelete.ts' | ||||
| import { applicationCommandUpdate } from './applicationCommandUpdate.ts' | ||||
| import type { SlashCommand } from '../../interactions/slashCommand.ts' | ||||
| import { MessageComponentInteraction } from '../../structures/messageComponents.ts' | ||||
| 
 | ||||
| export const gatewayHandlers: { | ||||
|   [eventCode in GatewayEvents]: GatewayEventHandler | undefined | ||||
|  | @ -364,7 +365,12 @@ export type ClientEvents = { | |||
|    * An Interaction was created | ||||
|    * @param interaction Created interaction object | ||||
|    */ | ||||
|   interactionCreate: [interaction: Interaction | SlashCommandInteraction] | ||||
|   interactionCreate: [ | ||||
|     interaction: | ||||
|       | Interaction | ||||
|       | SlashCommandInteraction | ||||
|       | MessageComponentInteraction | ||||
|   ] | ||||
| 
 | ||||
|   /** | ||||
|    * When debug message was made | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ import { User } from '../structures/user.ts' | |||
| import { HarmonyEventEmitter } from '../utils/events.ts' | ||||
| import { encodeText, decodeText } from '../utils/encoding.ts' | ||||
| import { SlashCommandsManager } from './slashCommand.ts' | ||||
| import { MessageComponentInteraction } from '../structures/messageComponents.ts' | ||||
| 
 | ||||
| export type SlashCommandHandlerCallback = (interaction: Interaction) => unknown | ||||
| export interface SlashCommandHandler { | ||||
|  | @ -77,8 +78,10 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> { | |||
| 
 | ||||
|     this.enabled = options.enabled ?? true | ||||
| 
 | ||||
|     if (this.client?._decoratedSlash !== undefined) { | ||||
|       this.client._decoratedSlash.forEach((e) => { | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     const client = this.client as any | ||||
|     if (client?._decoratedSlash !== undefined) { | ||||
|       client._decoratedSlash.forEach((e: any) => { | ||||
|         e.handler = e.handler.bind(this.client) | ||||
|         this.handlers.push(e) | ||||
|       }) | ||||
|  | @ -205,7 +208,10 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> { | |||
| 
 | ||||
|   /** Process an incoming Interaction */ | ||||
|   private async _process( | ||||
|     interaction: Interaction | SlashCommandInteraction | ||||
|     interaction: | ||||
|       | Interaction | ||||
|       | SlashCommandInteraction | ||||
|       | MessageComponentInteraction | ||||
|   ): Promise<void> { | ||||
|     if (!this.enabled) return | ||||
| 
 | ||||
|  | @ -282,7 +288,7 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> { | |||
|           member: payload.member as any, | ||||
|           guild: payload.guild_id as any, | ||||
|           channel: payload.channel_id as any, | ||||
|           resolved: ((payload.data | ||||
|           resolved: (((payload.data as any) | ||||
|             ?.resolved as unknown) as InteractionApplicationCommandResolved) ?? { | ||||
|             users: {}, | ||||
|             members: {}, | ||||
|  | @ -400,12 +406,14 @@ export class SlashClient extends HarmonyEventEmitter<SlashClientEvents> { | |||
| /** Decorator to create a Slash Command handler */ | ||||
| export function slash(name?: string, guild?: string) { | ||||
|   return function (client: Client | SlashClient | SlashModule, prop: string) { | ||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     const c = client as any | ||||
|     if (c._decoratedSlash === undefined) c._decoratedSlash = [] | ||||
|     const item = (client as { [name: string]: any })[prop] | ||||
|     if (typeof item !== 'function') { | ||||
|       throw new Error('@slash decorator requires a function') | ||||
|     } else | ||||
|       client._decoratedSlash.push({ | ||||
|       c._decoratedSlash.push({ | ||||
|         name: name ?? prop, | ||||
|         guild, | ||||
|         handler: item | ||||
|  | @ -416,12 +424,14 @@ export function slash(name?: string, guild?: string) { | |||
| /** Decorator to create a Sub-Slash Command handler */ | ||||
| export function subslash(parent: string, name?: string, guild?: string) { | ||||
|   return function (client: Client | SlashModule | SlashClient, prop: string) { | ||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     const c = client as any | ||||
|     if (c._decoratedSlash === undefined) c._decoratedSlash = [] | ||||
|     const item = (client as { [name: string]: any })[prop] | ||||
|     if (typeof item !== 'function') { | ||||
|       throw new Error('@subslash decorator requires a function') | ||||
|     } else | ||||
|       client._decoratedSlash.push({ | ||||
|       c._decoratedSlash.push({ | ||||
|         parent, | ||||
|         name: name ?? prop, | ||||
|         guild, | ||||
|  | @ -438,12 +448,14 @@ export function groupslash( | |||
|   guild?: string | ||||
| ) { | ||||
|   return function (client: Client | SlashModule | SlashClient, prop: string) { | ||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     const c = client as any | ||||
|     if (c._decoratedSlash === undefined) c._decoratedSlash = [] | ||||
|     const item = (client as { [name: string]: any })[prop] | ||||
|     if (typeof item !== 'function') { | ||||
|       throw new Error('@groupslash decorator requires a function') | ||||
|     } else | ||||
|       client._decoratedSlash.push({ | ||||
|       c._decoratedSlash.push({ | ||||
|         group, | ||||
|         parent, | ||||
|         name: name ?? prop, | ||||
|  |  | |||
							
								
								
									
										19
									
								
								src/managers/_util.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/managers/_util.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| import { | ||||
|   MessageComponentData, | ||||
|   MessageComponentPayload | ||||
| } from '../types/messageComponents.ts' | ||||
| 
 | ||||
| export function transformComponent( | ||||
|   d: MessageComponentData[] | ||||
| ): MessageComponentPayload[] { | ||||
|   return d.map((e: any) => { | ||||
|     if (e.customID !== undefined) { | ||||
|       e.custom_id = e.customID | ||||
|       delete e.customID | ||||
|     } | ||||
|     if (e.components !== undefined) { | ||||
|       e.components = transformComponent(e.components) | ||||
|     } | ||||
|     return e | ||||
|   }) | ||||
| } | ||||
|  | @ -11,6 +11,7 @@ import type { | |||
| import { CHANNEL } from '../types/endpoint.ts' | ||||
| import getChannelByType from '../utils/channel.ts' | ||||
| import { BaseManager } from './base.ts' | ||||
| import { transformComponent } from './_util.ts' | ||||
| 
 | ||||
| export type AllMessageOptions = MessageOptions | Embed | ||||
| 
 | ||||
|  | @ -100,7 +101,10 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> { | |||
|       content: content, | ||||
|       embed: option?.embed, | ||||
|       file: option?.file, | ||||
|       components: option?.components, | ||||
|       components: | ||||
|         option?.components !== undefined | ||||
|           ? transformComponent(option.components) | ||||
|           : undefined, | ||||
|       files: option?.files, | ||||
|       tts: option?.tts, | ||||
|       allowed_mentions: option?.allowedMentions, | ||||
|  | @ -168,6 +172,10 @@ export class ChannelsManager extends BaseManager<ChannelPayload, Channel> { | |||
|       embed: option?.embed !== undefined ? option.embed.toJSON() : undefined, | ||||
|       // Cannot upload new files with Message
 | ||||
|       // file: option?.file,
 | ||||
|       components: | ||||
|         option?.components !== undefined | ||||
|           ? transformComponent(option.components) | ||||
|           : undefined, | ||||
|       tts: option?.tts, | ||||
|       allowed_mentions: option?.allowedMentions | ||||
|     }) | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ export class DiscordAPIError extends Error { | |||
|     this.message = | ||||
|       typeof error === 'string' | ||||
|         ? `${error} ` | ||||
|         : `\n${error.method.toUpperCase()} ${error.url.slice(7)} returned ${ | ||||
|         : `\n${error.method.toUpperCase()} ${error.url} returned ${ | ||||
|             error.status | ||||
|           }\n(${error.code ?? 'unknown'}) ${error.message}${ | ||||
|             fmt.length === 0 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import type { Client } from '../client/client.ts' | ||||
| import { transformComponent } from '../managers/_util.ts' | ||||
| import { | ||||
|   AllowedMentionsPayload, | ||||
|   ChannelTypes, | ||||
|  | @ -13,11 +14,14 @@ import { | |||
|   InteractionResponseType, | ||||
|   InteractionType | ||||
| } from '../types/interactions.ts' | ||||
| import { | ||||
|   InteractionMessageComponentData, | ||||
|   MessageComponentData | ||||
| } from '../types/messageComponents.ts' | ||||
| import { | ||||
|   InteractionApplicationCommandData, | ||||
|   InteractionChannelPayload | ||||
| } from '../types/slashCommands.ts' | ||||
| import { Dict } from '../utils/dict.ts' | ||||
| import { Permissions } from '../utils/permissions.ts' | ||||
| import { SnowflakeBase } from './base.ts' | ||||
| import { Channel } from './channel.ts' | ||||
|  | @ -26,7 +30,6 @@ import { Guild } from './guild.ts' | |||
| import { GuildTextChannel } from './guildTextChannel.ts' | ||||
| import { Member } from './member.ts' | ||||
| import { Message } from './message.ts' | ||||
| import { Role } from './role.ts' | ||||
| import { TextChannel } from './textChannel.ts' | ||||
| import { User } from './user.ts' | ||||
| 
 | ||||
|  | @ -47,6 +50,7 @@ export interface InteractionMessageOptions { | |||
|   allowedMentions?: AllowedMentionsPayload | ||||
|   /** Whether the Message Response should be Ephemeral (only visible to User) or not */ | ||||
|   ephemeral?: boolean | ||||
|   components?: MessageComponentData[] | ||||
| } | ||||
| 
 | ||||
| export interface InteractionResponse extends InteractionMessageOptions { | ||||
|  | @ -76,13 +80,6 @@ export class InteractionChannel extends SnowflakeBase { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| export interface InteractionApplicationCommandResolved { | ||||
|   users: Dict<InteractionUser> | ||||
|   members: Dict<Member> | ||||
|   channels: Dict<InteractionChannel> | ||||
|   roles: Dict<Role> | ||||
| } | ||||
| 
 | ||||
| export class InteractionUser extends User { | ||||
|   member?: Member | ||||
| } | ||||
|  | @ -110,7 +107,8 @@ export class Interaction extends SnowflakeBase { | |||
|   _httpResponded?: boolean | ||||
|   applicationID: string | ||||
|   /** Data sent with Interaction. Only applies to Application Command */ | ||||
|   data?: InteractionApplicationCommandData | ||||
|   data?: InteractionApplicationCommandData | InteractionMessageComponentData | ||||
|   message?: Message | ||||
| 
 | ||||
|   constructor( | ||||
|     client: Client, | ||||
|  | @ -120,6 +118,7 @@ export class Interaction extends SnowflakeBase { | |||
|       guild?: Guild | ||||
|       member?: Member | ||||
|       user: User | ||||
|       message?: Message | ||||
|     } | ||||
|   ) { | ||||
|     super(client) | ||||
|  | @ -132,6 +131,7 @@ export class Interaction extends SnowflakeBase { | |||
|     this.data = data.data | ||||
|     this.guild = others.guild | ||||
|     this.channel = others.channel | ||||
|     this.message = others.message | ||||
|   } | ||||
| 
 | ||||
|   /** Respond to an Interaction */ | ||||
|  | @ -154,7 +154,11 @@ export class Interaction extends SnowflakeBase { | |||
|               embeds: data.embeds, | ||||
|               tts: data.tts ?? false, | ||||
|               flags, | ||||
|               allowed_mentions: data.allowedMentions ?? undefined | ||||
|               allowed_mentions: data.allowedMentions ?? undefined, | ||||
|               components: | ||||
|                 data.components === undefined | ||||
|                   ? undefined | ||||
|                   : transformComponent(data.components) | ||||
|             } | ||||
|           : undefined | ||||
|     } | ||||
|  | @ -227,6 +231,7 @@ export class Interaction extends SnowflakeBase { | |||
|     embeds?: Array<Embed | EmbedPayload> | ||||
|     flags?: number | number[] | ||||
|     allowedMentions?: AllowedMentionsPayload | ||||
|     components?: MessageComponentData[] | ||||
|   }): Promise<Interaction> { | ||||
|     const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') | ||||
|     await this.client.rest.patch(url, { | ||||
|  | @ -236,7 +241,11 @@ export class Interaction extends SnowflakeBase { | |||
|         typeof data.flags === 'object' | ||||
|           ? data.flags.reduce((p, a) => p | a, 0) | ||||
|           : data.flags, | ||||
|       allowed_mentions: data.allowedMentions | ||||
|       allowed_mentions: data.allowedMentions, | ||||
|       components: | ||||
|         data.components === undefined | ||||
|           ? undefined | ||||
|           : transformComponent(data.components) | ||||
|     }) | ||||
|     return this | ||||
|   } | ||||
|  |  | |||
							
								
								
									
										42
									
								
								src/structures/messageComponents.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/structures/messageComponents.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| import { | ||||
|   InteractionMessageComponentData, | ||||
|   MessageComponentType | ||||
| } from '../types/messageComponents.ts' | ||||
| import { Interaction } from './interactions.ts' | ||||
| import type { Client } from '../client/mod.ts' | ||||
| import { InteractionPayload } from '../types/interactions.ts' | ||||
| import type { Guild } from './guild.ts' | ||||
| import type { GuildTextChannel } from './guildTextChannel.ts' | ||||
| import type { Member } from './member.ts' | ||||
| import type { TextChannel } from './textChannel.ts' | ||||
| import { User } from './user.ts' | ||||
| import { Message } from './message.ts' | ||||
| 
 | ||||
| export class MessageComponentInteraction extends Interaction { | ||||
|   data: InteractionMessageComponentData | ||||
|   declare message: Message | ||||
| 
 | ||||
|   constructor( | ||||
|     client: Client, | ||||
|     data: InteractionPayload, | ||||
|     others: { | ||||
|       channel?: TextChannel | GuildTextChannel | ||||
|       guild?: Guild | ||||
|       member?: Member | ||||
|       user: User | ||||
|       message?: Message | ||||
|     } | ||||
|   ) { | ||||
|     super(client, data, others) | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|     this.data = data.data as InteractionMessageComponentData | ||||
|   } | ||||
| 
 | ||||
|   get customID(): string { | ||||
|     return this.data.custom_id | ||||
|   } | ||||
| 
 | ||||
|   get componentType(): MessageComponentType { | ||||
|     return this.data.component_type | ||||
|   } | ||||
| } | ||||
|  | @ -7,7 +7,10 @@ import type { EmojiPayload } from './emoji.ts' | |||
| import type { MemberPayload } from './guild.ts' | ||||
| import type { InteractionType } from './interactions.ts' | ||||
| import type { UserPayload } from './user.ts' | ||||
| import type { MessageComponentPayload } from './messageComponents.ts' | ||||
| import type { | ||||
|   MessageComponentData, | ||||
|   MessageComponentPayload | ||||
| } from './messageComponents.ts' | ||||
| 
 | ||||
| export interface ChannelPayload { | ||||
|   id: string | ||||
|  | @ -189,6 +192,7 @@ export interface MessagePayload { | |||
|   flags?: number | ||||
|   stickers?: MessageStickerPayload[] | ||||
|   interaction?: MessageInteractionPayload | ||||
|   components?: MessageComponentPayload[] | ||||
| } | ||||
| 
 | ||||
| export enum AllowedMentionType { | ||||
|  | @ -211,7 +215,7 @@ export interface MessageOptions { | |||
|   files?: MessageAttachment[] | ||||
|   allowedMentions?: AllowedMentionsPayload | ||||
|   reply?: Message | MessageReference | string | ||||
|   components?: MessageComponentPayload[] | ||||
|   components?: MessageComponentData[] | ||||
| } | ||||
| 
 | ||||
| export interface ChannelMention { | ||||
|  | @ -395,6 +399,7 @@ export interface EditMessagePayload { | |||
|   embed?: EmbedPayload | ||||
|   allowed_mentions?: AllowedMentionsPayload | ||||
|   flags?: number | ||||
|   components?: MessageComponentPayload[] | ||||
| } | ||||
| 
 | ||||
| export interface CreateMessagePayload extends EditMessagePayload { | ||||
|  |  | |||
|  | @ -1,5 +1,13 @@ | |||
| import { AllowedMentionsPayload, EmbedPayload } from './channel.ts' | ||||
| import { | ||||
|   AllowedMentionsPayload, | ||||
|   EmbedPayload, | ||||
|   MessagePayload | ||||
| } from './channel.ts' | ||||
| import type { MemberPayload } from './guild.ts' | ||||
| import { | ||||
|   InteractionMessageComponentData, | ||||
|   MessageComponentData | ||||
| } from './messageComponents.ts' | ||||
| import type { InteractionApplicationCommandData } from './slashCommands.ts' | ||||
| import type { UserPayload } from './user.ts' | ||||
| 
 | ||||
|  | @ -7,7 +15,9 @@ export enum InteractionType { | |||
|   /** Ping sent by the API (HTTP-only) */ | ||||
|   PING = 1, | ||||
|   /** Slash Command Interaction */ | ||||
|   APPLICATION_COMMAND = 2 | ||||
|   APPLICATION_COMMAND = 2, | ||||
|   /** Message Component Interaction */ | ||||
|   MESSAGE_COMPONENT = 3 | ||||
| } | ||||
| 
 | ||||
| export interface InteractionMemberPayload extends MemberPayload { | ||||
|  | @ -27,14 +37,17 @@ export interface InteractionPayload { | |||
|   /** ID of the Interaction */ | ||||
|   id: string | ||||
|   /** | ||||
|    * Data sent with the interaction. Undefined only when Interaction is not Slash Command.* | ||||
|    * Data sent with the interaction. Undefined only when Interaction is PING (http-only).* | ||||
|    */ | ||||
|   data?: InteractionApplicationCommandData | ||||
|   data?: InteractionApplicationCommandData | InteractionMessageComponentData | ||||
|   /** ID of the Guild in which Interaction was invoked */ | ||||
|   guild_id?: string | ||||
|   /** ID of the Channel in which Interaction was invoked */ | ||||
|   channel_id?: string | ||||
|   /** Application ID of the Client who received interaction */ | ||||
|   application_id: string | ||||
|   /** Message ID if the Interaction was of type MESSAGE_COMPONENT */ | ||||
|   message?: MessagePayload | ||||
| } | ||||
| 
 | ||||
| export enum InteractionResponseType { | ||||
|  | @ -62,6 +75,7 @@ export interface InteractionResponseDataPayload { | |||
|   /** Allowed Mentions object */ | ||||
|   allowed_mentions?: AllowedMentionsPayload | ||||
|   flags?: number | ||||
|   components?: MessageComponentData[] | ||||
| } | ||||
| 
 | ||||
| export enum InteractionResponseFlags { | ||||
|  |  | |||
|  | @ -17,4 +17,19 @@ export interface MessageComponentPayload { | |||
|   label?: string | ||||
|   style?: ButtonStyle | ||||
|   url?: string | ||||
|   custom_id?: string | ||||
| } | ||||
| 
 | ||||
| export interface MessageComponentData { | ||||
|   type: MessageComponentType | ||||
|   components?: MessageComponentData[] | ||||
|   label?: string | ||||
|   style?: ButtonStyle | ||||
|   url?: string | ||||
|   customID?: string | ||||
| } | ||||
| 
 | ||||
| export interface InteractionMessageComponentData { | ||||
|   custom_id: string | ||||
|   component_type: MessageComponentType | ||||
| } | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ export function simplifyAPIError(errors: any): SimplifiedError { | |||
|         const arrayIndex = !isNaN(Number(obj[0])) | ||||
|         if (arrayIndex) obj[0] = `[${obj[0]}]` | ||||
|         if (acum !== '' && !arrayIndex) acum += '.' | ||||
|         fmt(obj[1], (acum += obj[0])) | ||||
|         fmt(obj[1], acum + obj[0]) | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
|  |  | |||
							
								
								
									
										16
									
								
								src/utils/interactions.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/utils/interactions.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| import { InteractionType } from '../../mod.ts' | ||||
| import { Interaction } from '../structures/interactions.ts' | ||||
| import { MessageComponentInteraction } from '../structures/messageComponents.ts' | ||||
| import { SlashCommandInteraction } from '../structures/slash.ts' | ||||
| 
 | ||||
| export function isSlashCommandInteraction( | ||||
|   d: Interaction | ||||
| ): d is SlashCommandInteraction { | ||||
|   return d.type === InteractionType.APPLICATION_COMMAND | ||||
| } | ||||
| 
 | ||||
| export function isMessageComponentInteraction( | ||||
|   d: Interaction | ||||
| ): d is MessageComponentInteraction { | ||||
|   return d.type === InteractionType.MESSAGE_COMPONENT | ||||
| } | ||||
							
								
								
									
										184
									
								
								test/components.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								test/components.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,184 @@ | |||
| import { | ||||
|   CommandClient, | ||||
|   Command, | ||||
|   CommandContext, | ||||
|   ButtonStyle, | ||||
|   MessageComponentType, | ||||
|   isMessageComponentInteraction, | ||||
|   MessageComponentInteraction, | ||||
|   Message | ||||
| } from '../mod.ts' | ||||
| import { TOKEN } from './config.ts' | ||||
| 
 | ||||
| const client = new CommandClient({ | ||||
|   prefix: '.', | ||||
|   spacesAfterPrefix: true | ||||
| }) | ||||
| 
 | ||||
| enum Choice { | ||||
|   Rock, | ||||
|   Paper, | ||||
|   Scissor | ||||
| } | ||||
| 
 | ||||
| const games = new Map< | ||||
|   string, | ||||
|   { user: number; bot: number; msg: Message; txt: string } | ||||
| >() | ||||
| const components = [ | ||||
|   { | ||||
|     type: MessageComponentType.ActionRow, | ||||
|     components: [ | ||||
|       { | ||||
|         type: MessageComponentType.Button, | ||||
|         style: ButtonStyle.Primary, | ||||
|         label: 'Rock', | ||||
|         customID: 'rps::Rock' | ||||
|       }, | ||||
|       { | ||||
|         type: MessageComponentType.Button, | ||||
|         style: ButtonStyle.Primary, | ||||
|         label: 'Paper', | ||||
|         customID: 'rps::Paper' | ||||
|       }, | ||||
|       { | ||||
|         type: MessageComponentType.Button, | ||||
|         style: ButtonStyle.Primary, | ||||
|         label: 'Scissor', | ||||
|         customID: 'rps::Scissor' | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| ] | ||||
| 
 | ||||
| client.once('ready', () => { | ||||
|   console.log('Ready!') | ||||
| }) | ||||
| 
 | ||||
| client.commands.add( | ||||
|   class extends Command { | ||||
|     name = 'button' | ||||
| 
 | ||||
|     execute(ctx: CommandContext): void { | ||||
|       ctx.channel.send('Test Buttons', { | ||||
|         components: [ | ||||
|           { | ||||
|             type: MessageComponentType.ActionRow, | ||||
|             components: [ | ||||
|               { | ||||
|                 type: MessageComponentType.Button, | ||||
|                 label: 'Primary', | ||||
|                 style: ButtonStyle.Primary, | ||||
|                 customID: '1' | ||||
|               }, | ||||
|               { | ||||
|                 type: MessageComponentType.Button, | ||||
|                 label: 'Secondary', | ||||
|                 style: ButtonStyle.Secondary, | ||||
|                 customID: '2' | ||||
|               }, | ||||
|               { | ||||
|                 type: MessageComponentType.Button, | ||||
|                 label: 'Destructive', | ||||
|                 style: ButtonStyle.Destructive, | ||||
|                 customID: '3' | ||||
|               }, | ||||
|               { | ||||
|                 type: MessageComponentType.Button, | ||||
|                 label: 'Success', | ||||
|                 style: ButtonStyle.Success, | ||||
|                 customID: '4' | ||||
|               }, | ||||
|               { | ||||
|                 type: MessageComponentType.Button, | ||||
|                 label: 'Link', | ||||
|                 style: ButtonStyle.Link, | ||||
|                 url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         ] | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| ) | ||||
| 
 | ||||
| client.commands.add( | ||||
|   class extends Command { | ||||
|     name = 'play' | ||||
| 
 | ||||
|     execute(ctx: CommandContext): any { | ||||
|       if (games.has(ctx.author.id)) | ||||
|         return ctx.message.reply('You are already playing!') | ||||
|       ctx.channel | ||||
|         .send('Game starts now!', { | ||||
|           components | ||||
|         }) | ||||
|         .then((msg) => { | ||||
|           games.set(ctx.author.id, { | ||||
|             user: 0, | ||||
|             bot: 0, | ||||
|             msg, | ||||
|             txt: 'Game starts now!' | ||||
|           }) | ||||
|         }) | ||||
|     } | ||||
|   } | ||||
| ) | ||||
| 
 | ||||
| // client.on('raw', (e, d) => {
 | ||||
| //   if (e === 'INTERACTION_CREATE') console.log(e, d)
 | ||||
| // })
 | ||||
| 
 | ||||
| client.on('interactionCreate', (i) => { | ||||
|   if (isMessageComponentInteraction(i) === true) { | ||||
|     const d = i as MessageComponentInteraction | ||||
| 
 | ||||
|     if (d.customID.startsWith('rps::') === true) { | ||||
|       const game = games.get(d.user.id) | ||||
|       if (game === undefined) return | ||||
|       const choice = d.customID.split('::')[1] | ||||
|       const c: number = Number(Choice[choice as any]) | ||||
|       const rand = Math.floor(Math.random() * 2) | ||||
| 
 | ||||
|       game.txt += '\n\n' | ||||
|       game.txt += `You: ${choice}, Bot: ${Choice[rand]}` | ||||
|       let msg | ||||
|       if (rand === c) { | ||||
|         msg = 'Both chose ' + Choice[rand] + '!' | ||||
|       } else if ( | ||||
|         (rand === 0 && c === 2) || | ||||
|         (rand === 1 && c === 0) || | ||||
|         (rand === 2 && c === 1) | ||||
|       ) { | ||||
|         msg = 'Bot got one point!' | ||||
|         game.bot++ | ||||
|       } else { | ||||
|         msg = 'You got one point!' | ||||
|         game.user++ | ||||
|       } | ||||
|       game.txt += '\nInfo: ' + msg | ||||
| 
 | ||||
|       if (game.bot === 5 || game.user === 5) { | ||||
|         const won = game.bot === 5 ? 'Bot' : 'You' | ||||
|         game.msg.edit( | ||||
|           `${won} won!\n\n**Points:** You: ${game.user} | Bot: ${game.bot}`, | ||||
|           { | ||||
|             components: [] | ||||
|           } | ||||
|         ) | ||||
|         games.delete(d.user.id) | ||||
|       } else { | ||||
|         game.msg.edit( | ||||
|           `${game.txt}\n\n**Points:** You: ${game.user} | Bot: ${game.bot}`, | ||||
|           { | ||||
|             components | ||||
|           } | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }) | ||||
| 
 | ||||
| console.log('Connecting...') | ||||
| client.connect(TOKEN, ['GUILDS', 'GUILD_MESSAGES', 'DIRECT_MESSAGES']) | ||||
|  | @ -8,12 +8,12 @@ import { | |||
|   CommandContext, | ||||
|   Extension, | ||||
|   Collection, | ||||
|   GuildTextChannel | ||||
| } from '../../mod.ts' | ||||
|   GuildTextChannel, | ||||
|   slash, | ||||
|   SlashCommandInteraction | ||||
| } from '../mod.ts' | ||||
| import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts' | ||||
| import { Manager, Player } from 'https://deno.land/x/lavadeno/mod.ts' | ||||
| import { Interaction } from '../structures/slash.ts' | ||||
| import { slash } from '../client/mod.ts' | ||||
| // import { SlashCommandOptionType } from '../types/slash.ts'
 | ||||
| 
 | ||||
| export const nodes = [ | ||||
|  | @ -58,12 +58,12 @@ class MyClient extends CommandClient { | |||
|   } | ||||
| 
 | ||||
|   @subslash('cmd', 'sub-cmd-no-grp') | ||||
|   subCmdNoGroup(d: Interaction): void { | ||||
|   subCmdNoGroup(d: SlashCommandInteraction): void { | ||||
|     d.respond({ content: 'sub-cmd-no-group worked' }) | ||||
|   } | ||||
| 
 | ||||
|   @groupslash('cmd', 'sub-cmd-group', 'sub-cmd') | ||||
|   subCmdGroup(d: Interaction): void { | ||||
|   subCmdGroup(d: SlashCommandInteraction): void { | ||||
|     d.respond({ content: 'sub-cmd-group worked' }) | ||||
|   } | ||||
| 
 | ||||
|  | @ -79,7 +79,7 @@ class MyClient extends CommandClient { | |||
|   } | ||||
| 
 | ||||
|   @slash() | ||||
|   run(d: Interaction): void { | ||||
|   run(d: SlashCommandInteraction): void { | ||||
|     console.log(d.name) | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue