From 17e74dce4526e0b3192eb52a933a945bdc1c3921 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 22 Apr 2021 09:18:45 +0530 Subject: [PATCH 01/12] make interactions model better --- mod.ts | 3 +- src/gateway/handlers/interactionCreate.ts | 37 ++- src/gateway/handlers/mod.ts | 6 +- src/interactions/slashClient.ts | 70 +++-- src/interactions/slashCommand.ts | 2 +- src/managers/channels.ts | 1 + src/rest/endpoints.ts | 4 +- src/structures/interactions.ts | 354 ++++++++++++++++++++++ src/structures/message.ts | 2 +- src/structures/slash.ts | 338 +-------------------- src/types/channel.ts | 5 +- src/types/gateway.ts | 2 +- src/types/interactions.ts | 70 +++++ src/types/messageComponents.ts | 20 ++ src/types/{slash.ts => slashCommands.ts} | 72 +---- 15 files changed, 545 insertions(+), 441 deletions(-) create mode 100644 src/structures/interactions.ts create mode 100644 src/types/interactions.ts create mode 100644 src/types/messageComponents.ts rename src/types/{slash.ts => slashCommands.ts} (55%) diff --git a/mod.ts b/mod.ts index 8536510..d05784b 100644 --- a/mod.ts +++ b/mod.ts @@ -39,7 +39,8 @@ 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/types/slash.ts' +export * from './src/types/slashCommands.ts' +export * from './src/types/interactions.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/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts index 3d4c5d4..156441b 100644 --- a/src/gateway/handlers/interactionCreate.ts +++ b/src/gateway/handlers/interactionCreate.ts @@ -1,12 +1,18 @@ import { Guild } from '../../structures/guild.ts' import { Member } from '../../structures/member.ts' import { - Interaction, InteractionApplicationCommandResolved, - InteractionChannel + SlashCommandInteraction } from '../../structures/slash.ts' +import { + Interaction, + InteractionChannel +} from '../../structures/interactions.ts' import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts' -import { InteractionPayload } from '../../types/slash.ts' +import { + InteractionPayload, + InteractionType +} from '../../types/interactions.ts' import { UserPayload } from '../../types/user.ts' import { Permissions } from '../../utils/permissions.ts' import type { Gateway, GatewayEventHandler } from '../mod.ts' @@ -99,12 +105,23 @@ export const interactionCreate: GatewayEventHandler = async ( } } - const interaction = new Interaction(gateway.client, d, { - member, - guild, - channel, - user, - resolved - }) + let interaction + if (d.type === InteractionType.APPLICATION_COMMAND) { + interaction = new SlashCommandInteraction(gateway.client, d, { + member, + guild, + channel, + user, + resolved + }) + } else { + interaction = new Interaction(gateway.client, d, { + member, + guild, + channel, + user + }) + } + gateway.client.emit('interactionCreate', interaction) } diff --git a/src/gateway/handlers/mod.ts b/src/gateway/handlers/mod.ts index 1c299f3..dc649e8 100644 --- a/src/gateway/handlers/mod.ts +++ b/src/gateway/handlers/mod.ts @@ -59,7 +59,8 @@ import type { EveryTextChannelTypes } from '../../utils/channel.ts' import { interactionCreate } from './interactionCreate.ts' -import type { Interaction } from '../../structures/slash.ts' +import type { Interaction } from '../../structures/interactions.ts' +import type { SlashCommandInteraction } from '../../structures/slash.ts' import type { CommandContext } from '../../commands/command.ts' import type { RequestMethods } from '../../rest/types.ts' import type { PartialInvitePayload } from '../../types/invite.ts' @@ -358,11 +359,12 @@ export type ClientEvents = { * @param channel Channel of which Webhooks were updated */ webhooksUpdate: [guild: Guild, channel: GuildTextBasedChannel] + /** * An Interaction was created * @param interaction Created interaction object */ - interactionCreate: [interaction: Interaction] + interactionCreate: [interaction: Interaction | SlashCommandInteraction] /** * When debug message was made diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts index 29e808c..9efd4c9 100644 --- a/src/interactions/slashClient.ts +++ b/src/interactions/slashClient.ts @@ -1,13 +1,14 @@ import { - Interaction, + SlashCommandInteraction, InteractionApplicationCommandResolved } from '../structures/slash.ts' +import { Interaction } from '../structures/interactions.ts' import { InteractionPayload, InteractionResponsePayload, - InteractionType, - SlashCommandOptionType -} from '../types/slash.ts' + InteractionType +} from '../types/interactions.ts' +import { SlashCommandOptionType } from '../types/slashCommands.ts' import type { Client } from '../client/mod.ts' import { RESTManager } from '../rest/mod.ts' import { SlashModule } from './slashModule.ts' @@ -170,7 +171,9 @@ export class SlashClient extends HarmonyEventEmitter { } /** Get Handler for an Interaction. Supports nested sub commands and sub command groups. */ - private _getCommand(i: Interaction): SlashCommandHandler | undefined { + private _getCommand( + i: SlashCommandInteraction + ): SlashCommandHandler | undefined { return this.getHandlers().find((e) => { const hasGroupOrParent = e.group !== undefined || e.parent !== undefined const groupMatched = @@ -201,22 +204,22 @@ export class SlashClient extends HarmonyEventEmitter { } /** Process an incoming Interaction */ - private async _process(interaction: Interaction): Promise { + private async _process( + interaction: Interaction | SlashCommandInteraction + ): Promise { if (!this.enabled) return - if ( - interaction.type !== InteractionType.APPLICATION_COMMAND || - interaction.data === undefined - ) - return + if (interaction.type !== InteractionType.APPLICATION_COMMAND) return const cmd = - this._getCommand(interaction) ?? + this._getCommand(interaction as SlashCommandInteraction) ?? this.getHandlers().find((e) => e.name === '*') if (cmd?.group !== undefined) - interaction.data.options = interaction.data.options[0].options ?? [] + (interaction as SlashCommandInteraction).data.options = + (interaction as SlashCommandInteraction).data.options[0].options ?? [] if (cmd?.parent !== undefined) - interaction.data.options = interaction.data.options[0].options ?? [] + (interaction as SlashCommandInteraction).data.options = + (interaction as SlashCommandInteraction).data.options[0].options ?? [] if (cmd === undefined) return @@ -271,20 +274,31 @@ export class SlashClient extends HarmonyEventEmitter { const payload: InteractionPayload = JSON.parse(decodeText(rawbody)) // TODO: Maybe fix all this hackery going on here? - const res = new Interaction(this as any, payload, { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - user: new User(this as any, (payload.member?.user ?? payload.user)!), - member: payload.member as any, - guild: payload.guild_id as any, - channel: payload.channel_id as any, - resolved: ((payload.data - ?.resolved as unknown) as InteractionApplicationCommandResolved) ?? { - users: {}, - members: {}, - roles: {}, - channels: {} - } - }) + let res + if (payload.type === InteractionType.APPLICATION_COMMAND) { + res = new SlashCommandInteraction(this as any, payload, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + user: new User(this as any, (payload.member?.user ?? payload.user)!), + member: payload.member as any, + guild: payload.guild_id as any, + channel: payload.channel_id as any, + resolved: ((payload.data + ?.resolved as unknown) as InteractionApplicationCommandResolved) ?? { + users: {}, + members: {}, + roles: {}, + channels: {} + } + }) + } else { + res = new Interaction(this as any, payload, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + user: new User(this as any, (payload.member?.user ?? payload.user)!), + member: payload.member as any, + guild: payload.guild_id as any, + channel: payload.channel_id as any + }) + } res._httpRespond = async (d: InteractionResponsePayload | FormData) => await req.respond({ status: 200, diff --git a/src/interactions/slashCommand.ts b/src/interactions/slashCommand.ts index 26237d3..e079bb2 100644 --- a/src/interactions/slashCommand.ts +++ b/src/interactions/slashCommand.ts @@ -6,7 +6,7 @@ import { SlashCommandOptionType, SlashCommandPartial, SlashCommandPayload -} from '../types/slash.ts' +} from '../types/slashCommands.ts' import { Collection } from '../utils/collection.ts' import type { SlashClient, SlashCommandHandlerCallback } from './slashClient.ts' diff --git a/src/managers/channels.ts b/src/managers/channels.ts index 69efe6c..2caca4d 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -100,6 +100,7 @@ export class ChannelsManager extends BaseManager { content: content, embed: option?.embed, file: option?.file, + components: option?.components, files: option?.files, tts: option?.tts, allowed_mentions: option?.allowedMentions, diff --git a/src/rest/endpoints.ts b/src/rest/endpoints.ts index ff14299..31c70ce 100644 --- a/src/rest/endpoints.ts +++ b/src/rest/endpoints.ts @@ -30,11 +30,11 @@ import type { InviteWithMetadataPayload } from '../types/invite.ts' import type { RoleModifyPayload, RolePayload } from '../types/role.ts' +import type { InteractionResponsePayload } from '../types/interactions.ts' import type { - InteractionResponsePayload, SlashCommandPartial, SlashCommandPayload -} from '../types/slash.ts' +} from '../types/slashCommands.ts' import type { TemplatePayload } from '../types/template.ts' import type { UserPayload } from '../types/user.ts' import type { VoiceRegion } from '../types/voice.ts' diff --git a/src/structures/interactions.ts b/src/structures/interactions.ts new file mode 100644 index 0000000..dad5e0c --- /dev/null +++ b/src/structures/interactions.ts @@ -0,0 +1,354 @@ +import type { Client } from '../client/client.ts' +import { + AllowedMentionsPayload, + ChannelTypes, + EmbedPayload, + MessageOptions +} from '../types/channel.ts' +import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts' +import { + InteractionPayload, + InteractionResponseFlags, + InteractionResponsePayload, + InteractionResponseType, + InteractionType +} from '../types/interactions.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' +import { Embed } from './embed.ts' +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' + +interface WebhookMessageOptions extends MessageOptions { + embeds?: Array + name?: string + avatar?: string +} + +type AllWebhookMessageOptions = string | WebhookMessageOptions + +/** Interaction Message related Options */ +export interface InteractionMessageOptions { + content?: string + embeds?: Array + tts?: boolean + flags?: number | InteractionResponseFlags[] + allowedMentions?: AllowedMentionsPayload + /** Whether the Message Response should be Ephemeral (only visible to User) or not */ + ephemeral?: boolean +} + +export interface InteractionResponse extends InteractionMessageOptions { + /** Type of Interaction Response */ + type?: InteractionResponseType +} + +/** Represents a Channel Object for an Option in Slash Command */ +export class InteractionChannel extends SnowflakeBase { + /** Name of the Channel */ + name: string + /** Channel Type */ + type: ChannelTypes + permissions: Permissions + + constructor(client: Client, data: InteractionChannelPayload) { + super(client) + this.id = data.id + this.name = data.name + this.type = data.type + this.permissions = new Permissions(data.permissions) + } + + /** Resolve to actual Channel object if present in Cache */ + async resolve(): Promise { + return this.client.channels.get(this.id) + } +} + +export interface InteractionApplicationCommandResolved { + users: Dict + members: Dict + channels: Dict + roles: Dict +} + +export class InteractionUser extends User { + member?: Member +} + +export class Interaction extends SnowflakeBase { + /** Type of Interaction */ + type: InteractionType + /** Interaction Token */ + token: string + /** Interaction ID */ + id: string + /** Channel in which Interaction was initiated */ + channel?: TextChannel | GuildTextChannel + /** Guild in which Interaction was initiated */ + guild?: Guild + /** Member object of who initiated the Interaction */ + member?: Member + /** User object of who invoked Interaction */ + user: User + /** Whether we have responded to Interaction or not */ + responded: boolean = false + /** Whether response was deferred or not */ + deferred: boolean = false + _httpRespond?: (d: InteractionResponsePayload) => unknown + _httpResponded?: boolean + applicationID: string + /** Data sent with Interaction. Only applies to Application Command */ + data?: InteractionApplicationCommandData + + constructor( + client: Client, + data: InteractionPayload, + others: { + channel?: TextChannel | GuildTextChannel + guild?: Guild + member?: Member + user: User + } + ) { + super(client) + this.type = data.type + this.token = data.token + this.member = others.member + this.id = data.id + this.applicationID = data.application_id + this.user = others.user + this.data = data.data + this.guild = others.guild + this.channel = others.channel + } + + /** Respond to an Interaction */ + async respond(data: InteractionResponse): Promise { + if (this.responded) throw new Error('Already responded to Interaction') + let flags = 0 + if (data.ephemeral === true) flags |= InteractionResponseFlags.EPHEMERAL + if (data.flags !== undefined) { + if (Array.isArray(data.flags)) + flags = data.flags.reduce((p, a) => p | a, flags) + else if (typeof data.flags === 'number') flags |= data.flags + } + const payload: InteractionResponsePayload = { + type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: + data.type === undefined || + data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE + ? { + content: data.content ?? '', + embeds: data.embeds, + tts: data.tts ?? false, + flags, + allowed_mentions: data.allowedMentions ?? undefined + } + : undefined + } + + if (this._httpRespond !== undefined && this._httpResponded !== true) { + this._httpResponded = true + await this._httpRespond(payload) + } else + await this.client.rest.post( + INTERACTION_CALLBACK(this.id, this.token), + payload + ) + this.responded = true + + return this + } + + /** Defer the Interaction i.e. let the user know bot is processing and will respond later. You only have 15 minutes to edit the response! */ + async defer(ephemeral = false): Promise { + await this.respond({ + type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE, + flags: ephemeral ? 1 << 6 : 0 + }) + this.deferred = true + return this + } + + /** Reply with a Message to the Interaction */ + async reply(content: string): Promise + async reply(options: InteractionMessageOptions): Promise + async reply( + content: string, + options: InteractionMessageOptions + ): Promise + async reply( + content: string | InteractionMessageOptions, + messageOptions?: InteractionMessageOptions + ): Promise { + let options: InteractionMessageOptions | undefined = + typeof content === 'object' ? content : messageOptions + if ( + typeof content === 'object' && + messageOptions !== undefined && + options !== undefined + ) + Object.assign(options, messageOptions) + if (options === undefined) options = {} + if (typeof content === 'string') Object.assign(options, { content }) + + if (this.deferred && this.responded) { + await this.editResponse({ + content: options.content, + embeds: options.embeds, + flags: options.flags, + allowedMentions: options.allowedMentions + }) + } else + await this.respond( + Object.assign(options, { + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE + }) + ) + + return this + } + + /** Edit the original Interaction response */ + async editResponse(data: { + content?: string + embeds?: Array + flags?: number | number[] + allowedMentions?: AllowedMentionsPayload + }): Promise { + const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') + await this.client.rest.patch(url, { + content: data.content ?? '', + embeds: data.embeds ?? [], + flags: + typeof data.flags === 'object' + ? data.flags.reduce((p, a) => p | a, 0) + : data.flags, + allowed_mentions: data.allowedMentions + }) + return this + } + + /** Delete the original Interaction Response */ + async deleteResponse(): Promise { + const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') + await this.client.rest.delete(url) + return this + } + + get url(): string { + return `https://discord.com/api/v8/webhooks/${this.applicationID}/${this.token}` + } + + /** Send a followup message */ + async send( + text?: string | AllWebhookMessageOptions, + option?: AllWebhookMessageOptions + ): Promise { + if (typeof text === 'object') { + option = text + text = undefined + } + + if (text === undefined && option === undefined) { + throw new Error('Either text or option is necessary.') + } + + if (option instanceof Embed) + option = { + embeds: [option] + } + + const payload: any = { + content: text, + embeds: + (option as WebhookMessageOptions)?.embed !== undefined + ? [(option as WebhookMessageOptions).embed] + : (option as WebhookMessageOptions)?.embeds !== undefined + ? (option as WebhookMessageOptions).embeds + : undefined, + file: (option as WebhookMessageOptions)?.file, + files: (option as WebhookMessageOptions)?.files, + tts: (option as WebhookMessageOptions)?.tts, + allowed_mentions: (option as WebhookMessageOptions)?.allowedMentions + } + + if ((option as WebhookMessageOptions)?.name !== undefined) { + payload.username = (option as WebhookMessageOptions)?.name + } + + if ((option as WebhookMessageOptions)?.avatar !== undefined) { + payload.avatar = (option as WebhookMessageOptions)?.avatar + } + + if ( + payload.embeds !== undefined && + payload.embeds instanceof Array && + payload.embeds.length > 10 + ) + throw new Error( + `Cannot send more than 10 embeds through Interaction Webhook` + ) + + const resp = await this.client.rest.post(`${this.url}?wait=true`, payload) + + const res = new Message( + this.client, + resp, + (this as unknown) as TextChannel, + (this as unknown) as User + ) + await res.mentions.fromPayload(resp) + return res + } + + /** Edit a Followup message */ + async editMessage( + msg: Message | string, + data: { + content?: string + embeds?: Array + file?: any + allowed_mentions?: { + parse?: string + roles?: string[] + users?: string[] + everyone?: boolean + } + } + ): Promise { + await this.client.rest.patch( + WEBHOOK_MESSAGE( + this.applicationID, + this.token ?? this.client.token, + typeof msg === 'string' ? msg : msg.id + ), + data + ) + return this + } + + /** Delete a follow-up Message */ + async deleteMessage(msg: Message | string): Promise { + await this.client.rest.delete( + WEBHOOK_MESSAGE( + this.applicationID, + this.token ?? this.client.token, + typeof msg === 'string' ? msg : msg.id + ) + ) + return this + } +} diff --git a/src/structures/message.ts b/src/structures/message.ts index 1fd5e4e..5e81b82 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -20,7 +20,7 @@ import type { Guild } from './guild.ts' import { MessageReactionsManager } from '../managers/messageReactions.ts' import { MessageSticker } from './messageSticker.ts' import type { Emoji } from './emoji.ts' -import type { InteractionType } from '../types/slash.ts' +import type { InteractionType } from '../types/interactions.ts' import { encodeText } from '../utils/encoding.ts' type AllMessageOptions = MessageOptions | Embed diff --git a/src/structures/slash.ts b/src/structures/slash.ts index c222814..c23f476 100644 --- a/src/structures/slash.ts +++ b/src/structures/slash.ts @@ -1,80 +1,22 @@ import type { Client } from '../client/mod.ts' -import type { - AllowedMentionsPayload, - ChannelTypes, - EmbedPayload, - MessageOptions -} from '../types/channel.ts' -import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts' +import { InteractionPayload } from '../types/interactions.ts' import { InteractionApplicationCommandData, InteractionApplicationCommandOption, - InteractionChannelPayload, - InteractionPayload, - InteractionResponseFlags, - InteractionResponsePayload, - InteractionResponseType, - InteractionType, SlashCommandOptionType -} from '../types/slash.ts' +} from '../types/slashCommands.ts' import type { Dict } from '../utils/dict.ts' -import { Permissions } from '../utils/permissions.ts' -import { SnowflakeBase } from './base.ts' -import type { Channel } from './channel.ts' -import { Embed } from './embed.ts' import type { Guild } from './guild.ts' import type { GuildTextChannel } from './guildTextChannel.ts' import type { Member } from './member.ts' -import { Message } from './message.ts' import type { Role } from './role.ts' import type { TextChannel } from './textChannel.ts' import { User } from './user.ts' - -interface WebhookMessageOptions extends MessageOptions { - embeds?: Array - name?: string - avatar?: string -} - -type AllWebhookMessageOptions = string | WebhookMessageOptions - -/** Interaction Message related Options */ -export interface InteractionMessageOptions { - content?: string - embeds?: Array - tts?: boolean - flags?: number | InteractionResponseFlags[] - allowedMentions?: AllowedMentionsPayload - /** Whether the Message Response should be Ephemeral (only visible to User) or not */ - ephemeral?: boolean -} - -export interface InteractionResponse extends InteractionMessageOptions { - /** Type of Interaction Response */ - type?: InteractionResponseType -} - -/** Represents a Channel Object for an Option in Slash Command */ -export class InteractionChannel extends SnowflakeBase { - /** Name of the Channel */ - name: string - /** Channel Type */ - type: ChannelTypes - permissions: Permissions - - constructor(client: Client, data: InteractionChannelPayload) { - super(client) - this.id = data.id - this.name = data.name - this.type = data.type - this.permissions = new Permissions(data.permissions) - } - - /** Resolve to actual Channel object if present in Cache */ - async resolve(): Promise { - return this.client.channels.get(this.id) - } -} +import { + InteractionUser, + InteractionChannel, + Interaction +} from './interactions.ts' export interface InteractionApplicationCommandResolved { users: Dict @@ -83,36 +25,11 @@ export interface InteractionApplicationCommandResolved { roles: Dict } -export class InteractionUser extends User { - member?: Member -} - -export class Interaction extends SnowflakeBase { - /** Type of Interaction */ - type: InteractionType - /** Interaction Token */ - token: string - /** Interaction ID */ - id: string +export class SlashCommandInteraction extends Interaction { /** Data sent with Interaction. Only applies to Application Command */ - data?: InteractionApplicationCommandData - /** Channel in which Interaction was initiated */ - channel?: TextChannel | GuildTextChannel - /** Guild in which Interaction was initiated */ - guild?: Guild - /** Member object of who initiated the Interaction */ - member?: Member - /** User object of who invoked Interaction */ - user: User - /** Whether we have responded to Interaction or not */ - responded: boolean = false + data: InteractionApplicationCommandData /** Resolved data for Snowflakes in Slash Command Arguments */ resolved: InteractionApplicationCommandResolved - /** Whether response was deferred or not */ - deferred: boolean = false - _httpRespond?: (d: InteractionResponsePayload) => unknown - _httpResponded?: boolean - applicationID: string constructor( client: Client, @@ -125,26 +42,19 @@ export class Interaction extends SnowflakeBase { resolved: InteractionApplicationCommandResolved } ) { - super(client) - this.type = data.type - this.token = data.token - this.member = others.member - this.id = data.id - this.applicationID = data.application_id - this.user = others.user - this.data = data.data - this.guild = others.guild - this.channel = others.channel + super(client, data, others) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + this.data = data.data as InteractionApplicationCommandData this.resolved = others.resolved } /** Name of the Command Used (may change with future additions to Interactions!) */ - get name(): string | undefined { - return this.data?.name + get name(): string { + return this.data.name } get options(): InteractionApplicationCommandOption[] { - return this.data?.options ?? [] + return this.data.options ?? [] } /** Get an option by name */ @@ -162,222 +72,4 @@ export class Interaction extends SnowflakeBase { return this.resolved.channels[op.value] as any else return op.value } - - /** Respond to an Interaction */ - async respond(data: InteractionResponse): Promise { - if (this.responded) throw new Error('Already responded to Interaction') - let flags = 0 - if (data.ephemeral === true) flags |= InteractionResponseFlags.EPHEMERAL - if (data.flags !== undefined) { - if (Array.isArray(data.flags)) - flags = data.flags.reduce((p, a) => p | a, flags) - else if (typeof data.flags === 'number') flags |= data.flags - } - const payload: InteractionResponsePayload = { - type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: - data.type === undefined || - data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE - ? { - content: data.content ?? '', - embeds: data.embeds, - tts: data.tts ?? false, - flags, - allowed_mentions: data.allowedMentions ?? undefined - } - : undefined - } - - if (this._httpRespond !== undefined && this._httpResponded !== true) { - this._httpResponded = true - await this._httpRespond(payload) - } else - await this.client.rest.post( - INTERACTION_CALLBACK(this.id, this.token), - payload - ) - this.responded = true - - return this - } - - /** Defer the Interaction i.e. let the user know bot is processing and will respond later. You only have 15 minutes to edit the response! */ - async defer(ephemeral = false): Promise { - await this.respond({ - type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE, - flags: ephemeral ? 1 << 6 : 0 - }) - this.deferred = true - return this - } - - /** Reply with a Message to the Interaction */ - async reply(content: string): Promise - async reply(options: InteractionMessageOptions): Promise - async reply( - content: string, - options: InteractionMessageOptions - ): Promise - async reply( - content: string | InteractionMessageOptions, - messageOptions?: InteractionMessageOptions - ): Promise { - let options: InteractionMessageOptions | undefined = - typeof content === 'object' ? content : messageOptions - if ( - typeof content === 'object' && - messageOptions !== undefined && - options !== undefined - ) - Object.assign(options, messageOptions) - if (options === undefined) options = {} - if (typeof content === 'string') Object.assign(options, { content }) - - if (this.deferred && this.responded) { - await this.editResponse({ - content: options.content, - embeds: options.embeds, - flags: options.flags, - allowedMentions: options.allowedMentions - }) - } else - await this.respond( - Object.assign(options, { - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE - }) - ) - - return this - } - - /** Edit the original Interaction response */ - async editResponse(data: { - content?: string - embeds?: Array - flags?: number | number[] - allowedMentions?: AllowedMentionsPayload - }): Promise { - const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') - await this.client.rest.patch(url, { - content: data.content ?? '', - embeds: data.embeds ?? [], - flags: - typeof data.flags === 'object' - ? data.flags.reduce((p, a) => p | a, 0) - : data.flags, - allowed_mentions: data.allowedMentions - }) - return this - } - - /** Delete the original Interaction Response */ - async deleteResponse(): Promise { - const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') - await this.client.rest.delete(url) - return this - } - - get url(): string { - return `https://discord.com/api/v8/webhooks/${this.applicationID}/${this.token}` - } - - /** Send a followup message */ - async send( - text?: string | AllWebhookMessageOptions, - option?: AllWebhookMessageOptions - ): Promise { - if (typeof text === 'object') { - option = text - text = undefined - } - - if (text === undefined && option === undefined) { - throw new Error('Either text or option is necessary.') - } - - if (option instanceof Embed) - option = { - embeds: [option] - } - - const payload: any = { - content: text, - embeds: - (option as WebhookMessageOptions)?.embed !== undefined - ? [(option as WebhookMessageOptions).embed] - : (option as WebhookMessageOptions)?.embeds !== undefined - ? (option as WebhookMessageOptions).embeds - : undefined, - file: (option as WebhookMessageOptions)?.file, - files: (option as WebhookMessageOptions)?.files, - tts: (option as WebhookMessageOptions)?.tts, - allowed_mentions: (option as WebhookMessageOptions)?.allowedMentions - } - - if ((option as WebhookMessageOptions)?.name !== undefined) { - payload.username = (option as WebhookMessageOptions)?.name - } - - if ((option as WebhookMessageOptions)?.avatar !== undefined) { - payload.avatar = (option as WebhookMessageOptions)?.avatar - } - - if ( - payload.embeds !== undefined && - payload.embeds instanceof Array && - payload.embeds.length > 10 - ) - throw new Error( - `Cannot send more than 10 embeds through Interaction Webhook` - ) - - const resp = await this.client.rest.post(`${this.url}?wait=true`, payload) - - const res = new Message( - this.client, - resp, - (this as unknown) as TextChannel, - (this as unknown) as User - ) - await res.mentions.fromPayload(resp) - return res - } - - /** Edit a Followup message */ - async editMessage( - msg: Message | string, - data: { - content?: string - embeds?: Array - file?: any - allowed_mentions?: { - parse?: string - roles?: string[] - users?: string[] - everyone?: boolean - } - } - ): Promise { - await this.client.rest.patch( - WEBHOOK_MESSAGE( - this.applicationID, - this.token ?? this.client.token, - typeof msg === 'string' ? msg : msg.id - ), - data - ) - return this - } - - /** Delete a follow-up Message */ - async deleteMessage(msg: Message | string): Promise { - await this.client.rest.delete( - WEBHOOK_MESSAGE( - this.applicationID, - this.token ?? this.client.token, - typeof msg === 'string' ? msg : msg.id - ) - ) - return this - } } diff --git a/src/types/channel.ts b/src/types/channel.ts index 8db4e5e..a07e064 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -5,8 +5,9 @@ import type { Role } from '../structures/role.ts' import type { Permissions } from '../utils/permissions.ts' import type { EmojiPayload } from './emoji.ts' import type { MemberPayload } from './guild.ts' -import type { InteractionType } from './slash.ts' +import type { InteractionType } from './interactions.ts' import type { UserPayload } from './user.ts' +import type { MessageComponentPayload } from './messageComponents.ts' export interface ChannelPayload { id: string @@ -210,6 +211,7 @@ export interface MessageOptions { files?: MessageAttachment[] allowedMentions?: AllowedMentionsPayload reply?: Message | MessageReference | string + components?: MessageComponentPayload[] } export interface ChannelMention { @@ -401,6 +403,7 @@ export interface CreateMessagePayload extends EditMessagePayload { message_reference?: MessageReference file?: MessageAttachment files?: MessageAttachment[] + components?: MessageComponentPayload[] } export interface CreateWebhookMessageBasePayload { diff --git a/src/types/gateway.ts b/src/types/gateway.ts index d5c2a64..195ff07 100644 --- a/src/types/gateway.ts +++ b/src/types/gateway.ts @@ -11,7 +11,7 @@ import type { ClientStatus } from './presence.ts' import type { RolePayload } from './role.ts' -import type { SlashCommandPayload } from './slash.ts' +import type { SlashCommandPayload } from './slashCommands.ts' import type { UserPayload } from './user.ts' /** diff --git a/src/types/interactions.ts b/src/types/interactions.ts new file mode 100644 index 0000000..41962c0 --- /dev/null +++ b/src/types/interactions.ts @@ -0,0 +1,70 @@ +import { AllowedMentionsPayload, EmbedPayload } from './channel.ts' +import type { MemberPayload } from './guild.ts' +import type { InteractionApplicationCommandData } from './slashCommands.ts' +import type { UserPayload } from './user.ts' + +export enum InteractionType { + /** Ping sent by the API (HTTP-only) */ + PING = 1, + /** Slash Command Interaction */ + APPLICATION_COMMAND = 2 +} + +export interface InteractionMemberPayload extends MemberPayload { + /** Permissions of the Member who initiated Interaction (Guild-only) */ + permissions: string +} + +export interface InteractionPayload { + /** Type of the Interaction */ + type: InteractionType + /** Token of the Interaction to respond */ + token: string + /** Member object of user who invoked */ + member?: InteractionMemberPayload + /** User who initiated Interaction (only in DMs) */ + user?: UserPayload + /** ID of the Interaction */ + id: string + /** + * Data sent with the interaction. Undefined only when Interaction is not Slash Command.* + */ + data?: InteractionApplicationCommandData + /** 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: string +} + +export enum InteractionResponseType { + /** Just ack a ping, Http-only. */ + PONG = 1, + /** Send a channel message as response. */ + CHANNEL_MESSAGE_WITH_SOURCE = 4, + /** Let the user know bot is processing ("thinking") and you can edit the response later */ + DEFERRED_CHANNEL_MESSAGE = 5 +} + +export interface InteractionResponsePayload { + /** Type of the response */ + type: InteractionResponseType + /** Data to be sent with response. Optional for types: Pong, Acknowledge, Ack with Source */ + data?: InteractionResponseDataPayload +} + +export interface InteractionResponseDataPayload { + tts?: boolean + /** Text content of the Response (Message) */ + content: string + /** Upto 10 Embed Objects to send with Response */ + embeds?: EmbedPayload[] + /** Allowed Mentions object */ + allowed_mentions?: AllowedMentionsPayload + flags?: number +} + +export enum InteractionResponseFlags { + /** A Message which is only visible to Interaction User. */ + EPHEMERAL = 1 << 6 +} diff --git a/src/types/messageComponents.ts b/src/types/messageComponents.ts new file mode 100644 index 0000000..c666546 --- /dev/null +++ b/src/types/messageComponents.ts @@ -0,0 +1,20 @@ +export enum MessageComponentType { + ActionRow = 1, + Button = 2 +} + +export enum ButtonStyle { + Primary = 1, + Secondary = 2, + Success = 3, + Destructive = 4, + Link = 5 +} + +export interface MessageComponentPayload { + type: MessageComponentType + components?: MessageComponentPayload[] + label?: string + style?: ButtonStyle + url?: string +} diff --git a/src/types/slash.ts b/src/types/slashCommands.ts similarity index 55% rename from src/types/slash.ts rename to src/types/slashCommands.ts index 2791740..c0447c7 100644 --- a/src/types/slash.ts +++ b/src/types/slashCommands.ts @@ -1,9 +1,5 @@ import type { Dict } from '../utils/dict.ts' -import type { - AllowedMentionsPayload, - ChannelTypes, - EmbedPayload -} from './channel.ts' +import type { ChannelTypes } from './channel.ts' import type { MemberPayload } from './guild.ts' import type { RolePayload } from './role.ts' import type { UserPayload } from './user.ts' @@ -44,40 +40,6 @@ export interface InteractionApplicationCommandData { resolved?: InteractionApplicationCommandResolvedPayload } -export enum InteractionType { - /** Ping sent by the API (HTTP-only) */ - PING = 1, - /** Slash Command Interaction */ - APPLICATION_COMMAND = 2 -} - -export interface InteractionMemberPayload extends MemberPayload { - /** Permissions of the Member who initiated Interaction (Guild-only) */ - permissions: string -} - -export interface InteractionPayload { - /** Type of the Interaction */ - type: InteractionType - /** Token of the Interaction to respond */ - token: string - /** Member object of user who invoked */ - member?: InteractionMemberPayload - /** User who initiated Interaction (only in DMs) */ - user?: UserPayload - /** ID of the Interaction */ - id: string - /** - * Data sent with the interaction. Undefined only when Interaction is not Slash Command.* - */ - data?: InteractionApplicationCommandData - /** 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: string -} - export interface SlashCommandChoice { /** (Display) name of the Choice */ name: string @@ -131,35 +93,3 @@ export interface SlashCommandPayload extends SlashCommandPartial { /** Application ID */ application_id: string } - -export enum InteractionResponseType { - /** Just ack a ping, Http-only. */ - PONG = 1, - /** Send a channel message as response. */ - CHANNEL_MESSAGE_WITH_SOURCE = 4, - /** Let the user know bot is processing ("thinking") and you can edit the response later */ - DEFERRED_CHANNEL_MESSAGE = 5 -} - -export interface InteractionResponsePayload { - /** Type of the response */ - type: InteractionResponseType - /** Data to be sent with response. Optional for types: Pong, Acknowledge, Ack with Source */ - data?: InteractionResponseDataPayload -} - -export interface InteractionResponseDataPayload { - tts?: boolean - /** Text content of the Response (Message) */ - content: string - /** Upto 10 Embed Objects to send with Response */ - embeds?: EmbedPayload[] - /** Allowed Mentions object */ - allowed_mentions?: AllowedMentionsPayload - flags?: number -} - -export enum InteractionResponseFlags { - /** A Message which is only visible to Interaction User. */ - EPHEMERAL = 1 << 6 -} From b97ec3c225c2e7df6b6210c99daebd1aa1e483c8 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Fri, 23 Apr 2021 11:07:55 +0530 Subject: [PATCH 02/12] message component interactions --- mod.ts | 4 + src/client/client.ts | 30 ++-- src/commands/extension.ts | 18 +-- src/gateway/handlers/interactionCreate.ts | 87 +++++++--- src/gateway/handlers/mod.ts | 8 +- src/interactions/slashClient.ts | 32 ++-- src/managers/_util.ts | 19 +++ src/managers/channels.ts | 10 +- src/rest/error.ts | 2 +- src/structures/interactions.ts | 33 ++-- src/structures/messageComponents.ts | 42 +++++ src/types/channel.ts | 9 +- src/types/interactions.ts | 22 ++- src/types/messageComponents.ts | 15 ++ src/utils/err_fmt.ts | 2 +- src/utils/interactions.ts | 16 ++ test/components.ts | 184 ++++++++++++++++++++++ test/music.ts | 14 +- 18 files changed, 461 insertions(+), 86 deletions(-) create mode 100644 src/managers/_util.ts create mode 100644 src/structures/messageComponents.ts create mode 100644 src/utils/interactions.ts create mode 100644 test/components.ts diff --git a/mod.ts b/mod.ts index d05784b..0a9a01a 100644 --- a/mod.ts +++ b/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' diff --git a/src/client/client.ts b/src/client/client.ts index 457d77b..ea3650d 100644 --- a/src/client/client.ts +++ b/src/client/client.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 { /** 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 { 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 { } /** 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 } } diff --git a/src/commands/extension.ts b/src/commands/extension.ts index 80f4bc1..1a29be1 100644 --- a/src/commands/extension.ts +++ b/src/commands/extension.ts @@ -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 } } diff --git a/src/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts index 156441b..f99c7e1 100644 --- a/src/gateway/handlers/interactionCreate.ts +++ b/src/gateway/handlers/interactionCreate.ts @@ -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(d.channel_id)) ?? - (await gateway.client.channels.fetch(d.channel_id)) + const channel = await gateway.client.channels.get( + 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( + 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 }) } diff --git a/src/gateway/handlers/mod.ts b/src/gateway/handlers/mod.ts index dc649e8..561c898 100644 --- a/src/gateway/handlers/mod.ts +++ b/src/gateway/handlers/mod.ts @@ -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 diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts index 9efd4c9..e254c16 100644 --- a/src/interactions/slashClient.ts +++ b/src/interactions/slashClient.ts @@ -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 { 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 { /** Process an incoming Interaction */ private async _process( - interaction: Interaction | SlashCommandInteraction + interaction: + | Interaction + | SlashCommandInteraction + | MessageComponentInteraction ): Promise { if (!this.enabled) return @@ -282,7 +288,7 @@ export class SlashClient extends HarmonyEventEmitter { 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 { /** 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, diff --git a/src/managers/_util.ts b/src/managers/_util.ts new file mode 100644 index 0000000..8b5b670 --- /dev/null +++ b/src/managers/_util.ts @@ -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 + }) +} diff --git a/src/managers/channels.ts b/src/managers/channels.ts index 2caca4d..6810ecb 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -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 { 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 { 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 }) diff --git a/src/rest/error.ts b/src/rest/error.ts index 3f4d4ed..1063f43 100644 --- a/src/rest/error.ts +++ b/src/rest/error.ts @@ -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 diff --git a/src/structures/interactions.ts b/src/structures/interactions.ts index dad5e0c..f848012 100644 --- a/src/structures/interactions.ts +++ b/src/structures/interactions.ts @@ -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 - members: Dict - channels: Dict - roles: Dict -} - 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 flags?: number | number[] allowedMentions?: AllowedMentionsPayload + components?: MessageComponentData[] }): Promise { 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 } diff --git a/src/structures/messageComponents.ts b/src/structures/messageComponents.ts new file mode 100644 index 0000000..1e47bd3 --- /dev/null +++ b/src/structures/messageComponents.ts @@ -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 + } +} diff --git a/src/types/channel.ts b/src/types/channel.ts index a07e064..f6542b5 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -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 { diff --git a/src/types/interactions.ts b/src/types/interactions.ts index 41962c0..01c12a4 100644 --- a/src/types/interactions.ts +++ b/src/types/interactions.ts @@ -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 { diff --git a/src/types/messageComponents.ts b/src/types/messageComponents.ts index c666546..22d344f 100644 --- a/src/types/messageComponents.ts +++ b/src/types/messageComponents.ts @@ -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 } diff --git a/src/utils/err_fmt.ts b/src/utils/err_fmt.ts index 5bd8672..9bf1609 100644 --- a/src/utils/err_fmt.ts +++ b/src/utils/err_fmt.ts @@ -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]) }) } } diff --git a/src/utils/interactions.ts b/src/utils/interactions.ts new file mode 100644 index 0000000..0849e72 --- /dev/null +++ b/src/utils/interactions.ts @@ -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 +} diff --git a/test/components.ts b/test/components.ts new file mode 100644 index 0000000..61208f0 --- /dev/null +++ b/test/components.ts @@ -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']) diff --git a/test/music.ts b/test/music.ts index ea7a463..c25aa31 100644 --- a/test/music.ts +++ b/test/music.ts @@ -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) } From 67d81c1bf0bad40329ac3589ce9aa962698e87ab Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sat, 24 Apr 2021 14:33:15 +0530 Subject: [PATCH 03/12] x --- src/interactions/slashModule.ts | 5 ++--- src/types/messageComponents.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/interactions/slashModule.ts b/src/interactions/slashModule.ts index 2ba0fe3..1c61c65 100644 --- a/src/interactions/slashModule.ts +++ b/src/interactions/slashModule.ts @@ -3,11 +3,10 @@ import type { SlashCommandHandler } from './slashClient.ts' export class SlashModule { name: string = '' commands: SlashCommandHandler[] = [] - _decoratedSlash?: SlashCommandHandler[] constructor() { - if (this._decoratedSlash !== undefined) { - this.commands = this._decoratedSlash + if ((this as any)._decoratedSlash !== undefined) { + ;(this as any).commands = (this as any)._decoratedSlash } } diff --git a/src/types/messageComponents.ts b/src/types/messageComponents.ts index 22d344f..45f3534 100644 --- a/src/types/messageComponents.ts +++ b/src/types/messageComponents.ts @@ -11,6 +11,12 @@ export enum ButtonStyle { Link = 5 } +export interface MessageComponentEmoji { + id?: string + name?: string + animated?: boolean +} + export interface MessageComponentPayload { type: MessageComponentType components?: MessageComponentPayload[] @@ -18,6 +24,7 @@ export interface MessageComponentPayload { style?: ButtonStyle url?: string custom_id?: string + emoji?: MessageComponentEmoji } export interface MessageComponentData { @@ -27,6 +34,7 @@ export interface MessageComponentData { style?: ButtonStyle url?: string customID?: string + emoji?: MessageComponentEmoji } export interface InteractionMessageComponentData { From 381cb13e6c5c8d863493796cd7f920b79e29f2a1 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sat, 24 Apr 2021 03:17:06 -0700 Subject: [PATCH 04/12] add disabled to component --- src/types/messageComponents.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/messageComponents.ts b/src/types/messageComponents.ts index 45f3534..3a0b21f 100644 --- a/src/types/messageComponents.ts +++ b/src/types/messageComponents.ts @@ -25,6 +25,7 @@ export interface MessageComponentPayload { url?: string custom_id?: string emoji?: MessageComponentEmoji + disabled?: boolean } export interface MessageComponentData { @@ -35,6 +36,7 @@ export interface MessageComponentData { url?: string customID?: string emoji?: MessageComponentEmoji + disabled?: boolean } export interface InteractionMessageComponentData { From 45bfbff3a334a7935d6d61348ee57ce1aed177ca Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Apr 2021 15:17:37 +0530 Subject: [PATCH 05/12] fix deploy --- deploy.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/deploy.ts b/deploy.ts index 7684b41..1c4df2b 100644 --- a/deploy.ts +++ b/deploy.ts @@ -1,10 +1,14 @@ +import { Interaction } from './mod.ts' import { SlashCommandsManager, SlashClient, SlashCommandHandlerCallback, SlashCommandHandler } from './src/interactions/mod.ts' -import { InteractionResponseType, InteractionType } from './src/types/slash.ts' +import { + InteractionResponseType, + InteractionType +} from './src/types/interactions.ts' export interface DeploySlashInitOptions { env?: boolean @@ -124,8 +128,18 @@ export function handle( client.handle(cmd, handler) } +export function interactions(cb: (i: Interaction) => any): void { + client.on('interaction', cb) +} + export { commands, client } -export * from './src/types/slash.ts' +export * from './src/types/slashCommands.ts' +export * from './src/types/interactions.ts' export * from './src/structures/slash.ts' export * from './src/interactions/mod.ts' export * from './src/types/channel.ts' +export * from './src/types/messageComponents.ts' +export * from './src/structures/interactions.ts' +export * from './src/structures/messageComponents.ts' +export * from './src/structures/message.ts' +export * from './src/structures/embed.ts' From a7c442d340165afa8e2c5a2917289d73b5b9517a Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Apr 2021 16:00:06 +0530 Subject: [PATCH 06/12] x --- src/interactions/slashClient.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts index 3c95c45..5f5473d 100644 --- a/src/interactions/slashClient.ts +++ b/src/interactions/slashClient.ts @@ -19,7 +19,9 @@ 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 type SlashCommandHandlerCallback = ( + interaction: SlashCommandInteraction +) => unknown export interface SlashCommandHandler { name: string guild?: string From 3fe3a849288f462fbce2fe09910a0517ada0473f Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Apr 2021 16:04:13 +0530 Subject: [PATCH 07/12] fix --- src/interactions/slashClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts index 5f5473d..a34c0c8 100644 --- a/src/interactions/slashClient.ts +++ b/src/interactions/slashClient.ts @@ -226,7 +226,7 @@ export class SlashClient extends HarmonyEventEmitter { await this.emit('interaction', interaction) try { - await cmd.handler(interaction) + await cmd.handler(interaction as SlashCommandInteraction) } catch (e) { await this.emit('interactionError', e) } From 332f0f2742e5eb834f3b0e049561dbf40bed15df Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Apr 2021 16:27:34 +0530 Subject: [PATCH 08/12] add support got path --- deploy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy.ts b/deploy.ts index 1c4df2b..307ee33 100644 --- a/deploy.ts +++ b/deploy.ts @@ -14,6 +14,7 @@ export interface DeploySlashInitOptions { env?: boolean publicKey?: string token?: string + path?: string } /** Current Slash Client being used to handle commands */ @@ -64,6 +65,9 @@ export function init(options: DeploySlashInitOptions): void { respondWith: CallableFunction request: Request }): Promise => { + if (options.path !== undefined) { + if (new URL(evt.request.url).pathname !== options.path) return + } try { // we have to wrap because there are some weird scope errors const d = await client.verifyFetchEvent({ From 1beccb57bae8265759fc0853c6574a0c7fe2f8d0 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Apr 2021 16:28:48 +0530 Subject: [PATCH 09/12] fix --- deploy.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/deploy.ts b/deploy.ts index 307ee33..793ff82 100644 --- a/deploy.ts +++ b/deploy.ts @@ -42,8 +42,12 @@ let commands: SlashCommandsManager * * @param options Initialization options */ -export function init(options: { env: boolean }): void -export function init(options: { publicKey: string; token?: string }): void +export function init(options: { env: boolean; path?: string }): void +export function init(options: { + publicKey: string + token?: string + path?: string +}): void export function init(options: DeploySlashInitOptions): void { if (client !== undefined) throw new Error('Already initialized') if (options.env === true) { From 2cdb671fb142421c6cc24e5aa312dd05fa3991ba Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 28 Apr 2021 16:54:22 +0530 Subject: [PATCH 10/12] interactions api design fix --- mod.ts | 2 - src/gateway/handlers/interactionCreate.ts | 9 ----- src/gateway/handlers/mod.ts | 8 +--- src/interactions/slashClient.ts | 6 +-- src/managers/_util.ts | 19 ---------- src/managers/channels.ts | 9 ----- src/structures/interactions.ts | 21 ++--------- src/structures/messageComponents.ts | 42 --------------------- src/types/channel.ts | 8 ---- src/types/interactions.ts | 11 +----- src/types/messageComponents.ts | 45 ----------------------- src/utils/interactions.ts | 7 ---- 12 files changed, 7 insertions(+), 180 deletions(-) delete mode 100644 src/structures/messageComponents.ts delete mode 100644 src/types/messageComponents.ts diff --git a/mod.ts b/mod.ts index 5df0bbb..c7caa24 100644 --- a/mod.ts +++ b/mod.ts @@ -40,10 +40,8 @@ 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' diff --git a/src/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts index f99c7e1..e533a5e 100644 --- a/src/gateway/handlers/interactionCreate.ts +++ b/src/gateway/handlers/interactionCreate.ts @@ -22,7 +22,6 @@ 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, @@ -156,14 +155,6 @@ 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, diff --git a/src/gateway/handlers/mod.ts b/src/gateway/handlers/mod.ts index 561c898..dc649e8 100644 --- a/src/gateway/handlers/mod.ts +++ b/src/gateway/handlers/mod.ts @@ -69,7 +69,6 @@ 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 @@ -365,12 +364,7 @@ export type ClientEvents = { * An Interaction was created * @param interaction Created interaction object */ - interactionCreate: [ - interaction: - | Interaction - | SlashCommandInteraction - | MessageComponentInteraction - ] + interactionCreate: [interaction: Interaction | SlashCommandInteraction] /** * When debug message was made diff --git a/src/interactions/slashClient.ts b/src/interactions/slashClient.ts index a34c0c8..4db556a 100644 --- a/src/interactions/slashClient.ts +++ b/src/interactions/slashClient.ts @@ -17,7 +17,6 @@ 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: SlashCommandInteraction @@ -203,10 +202,7 @@ export class SlashClient extends HarmonyEventEmitter { /** Process an incoming Interaction */ private async _process( - interaction: - | Interaction - | SlashCommandInteraction - | MessageComponentInteraction + interaction: Interaction | SlashCommandInteraction ): Promise { if (!this.enabled) return diff --git a/src/managers/_util.ts b/src/managers/_util.ts index 8b5b670..e69de29 100644 --- a/src/managers/_util.ts +++ b/src/managers/_util.ts @@ -1,19 +0,0 @@ -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 - }) -} diff --git a/src/managers/channels.ts b/src/managers/channels.ts index 6810ecb..69efe6c 100644 --- a/src/managers/channels.ts +++ b/src/managers/channels.ts @@ -11,7 +11,6 @@ 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 @@ -101,10 +100,6 @@ export class ChannelsManager extends BaseManager { content: content, embed: option?.embed, file: option?.file, - components: - option?.components !== undefined - ? transformComponent(option.components) - : undefined, files: option?.files, tts: option?.tts, allowed_mentions: option?.allowedMentions, @@ -172,10 +167,6 @@ export class ChannelsManager extends BaseManager { 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 }) diff --git a/src/structures/interactions.ts b/src/structures/interactions.ts index f848012..86001e9 100644 --- a/src/structures/interactions.ts +++ b/src/structures/interactions.ts @@ -1,5 +1,4 @@ import type { Client } from '../client/client.ts' -import { transformComponent } from '../managers/_util.ts' import { AllowedMentionsPayload, ChannelTypes, @@ -14,10 +13,6 @@ import { InteractionResponseType, InteractionType } from '../types/interactions.ts' -import { - InteractionMessageComponentData, - MessageComponentData -} from '../types/messageComponents.ts' import { InteractionApplicationCommandData, InteractionChannelPayload @@ -50,7 +45,6 @@ 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 { @@ -107,7 +101,7 @@ export class Interaction extends SnowflakeBase { _httpResponded?: boolean applicationID: string /** Data sent with Interaction. Only applies to Application Command */ - data?: InteractionApplicationCommandData | InteractionMessageComponentData + data?: InteractionApplicationCommandData message?: Message constructor( @@ -154,11 +148,7 @@ export class Interaction extends SnowflakeBase { embeds: data.embeds, tts: data.tts ?? false, flags, - allowed_mentions: data.allowedMentions ?? undefined, - components: - data.components === undefined - ? undefined - : transformComponent(data.components) + allowed_mentions: data.allowedMentions ?? undefined } : undefined } @@ -231,7 +221,6 @@ export class Interaction extends SnowflakeBase { embeds?: Array flags?: number | number[] allowedMentions?: AllowedMentionsPayload - components?: MessageComponentData[] }): Promise { const url = WEBHOOK_MESSAGE(this.applicationID, this.token, '@original') await this.client.rest.patch(url, { @@ -241,11 +230,7 @@ export class Interaction extends SnowflakeBase { typeof data.flags === 'object' ? data.flags.reduce((p, a) => p | a, 0) : data.flags, - allowed_mentions: data.allowedMentions, - components: - data.components === undefined - ? undefined - : transformComponent(data.components) + allowed_mentions: data.allowedMentions }) return this } diff --git a/src/structures/messageComponents.ts b/src/structures/messageComponents.ts deleted file mode 100644 index 1e47bd3..0000000 --- a/src/structures/messageComponents.ts +++ /dev/null @@ -1,42 +0,0 @@ -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 - } -} diff --git a/src/types/channel.ts b/src/types/channel.ts index f6542b5..25eb484 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -7,10 +7,6 @@ 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 { - MessageComponentData, - MessageComponentPayload -} from './messageComponents.ts' export interface ChannelPayload { id: string @@ -192,7 +188,6 @@ export interface MessagePayload { flags?: number stickers?: MessageStickerPayload[] interaction?: MessageInteractionPayload - components?: MessageComponentPayload[] } export enum AllowedMentionType { @@ -215,7 +210,6 @@ export interface MessageOptions { files?: MessageAttachment[] allowedMentions?: AllowedMentionsPayload reply?: Message | MessageReference | string - components?: MessageComponentData[] } export interface ChannelMention { @@ -399,7 +393,6 @@ export interface EditMessagePayload { embed?: EmbedPayload allowed_mentions?: AllowedMentionsPayload flags?: number - components?: MessageComponentPayload[] } export interface CreateMessagePayload extends EditMessagePayload { @@ -408,7 +401,6 @@ export interface CreateMessagePayload extends EditMessagePayload { message_reference?: MessageReference file?: MessageAttachment files?: MessageAttachment[] - components?: MessageComponentPayload[] } export interface CreateWebhookMessageBasePayload { diff --git a/src/types/interactions.ts b/src/types/interactions.ts index 01c12a4..e4d4d94 100644 --- a/src/types/interactions.ts +++ b/src/types/interactions.ts @@ -4,10 +4,6 @@ import { 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' @@ -15,9 +11,7 @@ export enum InteractionType { /** Ping sent by the API (HTTP-only) */ PING = 1, /** Slash Command Interaction */ - APPLICATION_COMMAND = 2, - /** Message Component Interaction */ - MESSAGE_COMPONENT = 3 + APPLICATION_COMMAND = 2 } export interface InteractionMemberPayload extends MemberPayload { @@ -39,7 +33,7 @@ export interface InteractionPayload { /** * Data sent with the interaction. Undefined only when Interaction is PING (http-only).* */ - data?: InteractionApplicationCommandData | InteractionMessageComponentData + data?: InteractionApplicationCommandData /** ID of the Guild in which Interaction was invoked */ guild_id?: string /** ID of the Channel in which Interaction was invoked */ @@ -75,7 +69,6 @@ export interface InteractionResponseDataPayload { /** Allowed Mentions object */ allowed_mentions?: AllowedMentionsPayload flags?: number - components?: MessageComponentData[] } export enum InteractionResponseFlags { diff --git a/src/types/messageComponents.ts b/src/types/messageComponents.ts deleted file mode 100644 index 3a0b21f..0000000 --- a/src/types/messageComponents.ts +++ /dev/null @@ -1,45 +0,0 @@ -export enum MessageComponentType { - ActionRow = 1, - Button = 2 -} - -export enum ButtonStyle { - Primary = 1, - Secondary = 2, - Success = 3, - Destructive = 4, - Link = 5 -} - -export interface MessageComponentEmoji { - id?: string - name?: string - animated?: boolean -} - -export interface MessageComponentPayload { - type: MessageComponentType - components?: MessageComponentPayload[] - label?: string - style?: ButtonStyle - url?: string - custom_id?: string - emoji?: MessageComponentEmoji - disabled?: boolean -} - -export interface MessageComponentData { - type: MessageComponentType - components?: MessageComponentData[] - label?: string - style?: ButtonStyle - url?: string - customID?: string - emoji?: MessageComponentEmoji - disabled?: boolean -} - -export interface InteractionMessageComponentData { - custom_id: string - component_type: MessageComponentType -} diff --git a/src/utils/interactions.ts b/src/utils/interactions.ts index 0849e72..c5d4249 100644 --- a/src/utils/interactions.ts +++ b/src/utils/interactions.ts @@ -1,6 +1,5 @@ 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( @@ -8,9 +7,3 @@ export function isSlashCommandInteraction( ): d is SlashCommandInteraction { return d.type === InteractionType.APPLICATION_COMMAND } - -export function isMessageComponentInteraction( - d: Interaction -): d is MessageComponentInteraction { - return d.type === InteractionType.MESSAGE_COMPONENT -} From c88e4a67b2b0ce0372438a1473900e516f9be75d Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 28 Apr 2021 16:57:57 +0530 Subject: [PATCH 11/12] oops wrong file commit --- test/components.ts | 184 --------------------------------------------- 1 file changed, 184 deletions(-) delete mode 100644 test/components.ts diff --git a/test/components.ts b/test/components.ts deleted file mode 100644 index 61208f0..0000000 --- a/test/components.ts +++ /dev/null @@ -1,184 +0,0 @@ -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']) From b22563ffc382142655318c17821962b5893fa8b6 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sat, 1 May 2021 10:33:08 +0530 Subject: [PATCH 12/12] requested changes --- src/gateway/handlers/interactionCreate.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts index e533a5e..43a80bb 100644 --- a/src/gateway/handlers/interactionCreate.ts +++ b/src/gateway/handlers/interactionCreate.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ import { Guild } from '../../structures/guild.ts' import { Member } from '../../structures/member.ts' import { @@ -19,7 +20,10 @@ 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 { + InteractionApplicationCommandData, + InteractionChannelPayload +} from '../../types/slashCommands.ts' import { Message } from '../../structures/message.ts' import { TextChannel } from '../../structures/textChannel.ts' @@ -71,7 +75,7 @@ export const interactionCreate: GatewayEventHandler = async ( roles: {} } - if ((d.data as any)?.resolved !== undefined) { + if ((d.data as InteractionApplicationCommandData)?.resolved !== undefined) { for (const [id, data] of Object.entries( (d.data as any)?.resolved.users ?? {} )) { @@ -84,7 +88,7 @@ export const interactionCreate: GatewayEventHandler = async ( } for (const [id, data] of Object.entries( - (d.data as any)?.resolved.members ?? {} + (d.data as InteractionApplicationCommandData)?.resolved?.members ?? {} )) { const roles = await guild?.roles.array() let permissions = new Permissions(Permissions.DEFAULT) @@ -109,7 +113,7 @@ export const interactionCreate: GatewayEventHandler = async ( } for (const [id, data] of Object.entries( - (d.data as any).resolved.roles ?? {} + (d.data as InteractionApplicationCommandData).resolved?.roles ?? {} )) { if (guild !== undefined) { await guild.roles.set(id, data as RolePayload) @@ -124,7 +128,7 @@ export const interactionCreate: GatewayEventHandler = async ( } for (const [id, data] of Object.entries( - (d.data as any).resolved.channels ?? {} + (d.data as InteractionApplicationCommandData).resolved?.channels ?? {} )) { resolved.channels[id] = new InteractionChannel( gateway.client,