diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 85e1d75..dbb2737 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -53,6 +53,7 @@ import { EveryChannelTypes, EveryTextChannelTypes } from '../../utils/getChannelByType.ts' +import { interactionCreate } from './interactionCreate.ts' export const gatewayHandlers: { [eventCode in GatewayEvents]: GatewayEventHandler | undefined @@ -93,7 +94,8 @@ export const gatewayHandlers: { USER_UPDATE: userUpdate, VOICE_STATE_UPDATE: voiceStateUpdate, VOICE_SERVER_UPDATE: voiceServerUpdate, - WEBHOOKS_UPDATE: webhooksUpdate + WEBHOOKS_UPDATE: webhooksUpdate, + INTERACTION_CREATE: interactionCreate } export interface EventTypes { diff --git a/src/gateway/handlers/interactionCreate.ts b/src/gateway/handlers/interactionCreate.ts new file mode 100644 index 0000000..7ff74fd --- /dev/null +++ b/src/gateway/handlers/interactionCreate.ts @@ -0,0 +1,11 @@ +import { Interaction } from '../../structures/slash.ts' +import { InteractionPayload } from '../../types/slash.ts' +import { Gateway, GatewayEventHandler } from '../index.ts' + +export const interactionCreate: GatewayEventHandler = async ( + gateway: Gateway, + d: InteractionPayload +) => { + const interaction = new Interaction(gateway.client, d) + gateway.client.emit('interactionCreate', interaction) +} diff --git a/src/structures/presence.ts b/src/structures/presence.ts index 85593fb..a5c86af 100644 --- a/src/structures/presence.ts +++ b/src/structures/presence.ts @@ -91,7 +91,6 @@ export class ClientPresence { } create(): StatusPayload { - console.log(this) return { afk: this.afk === undefined ? false : this.afk, activities: this.createActivity(), diff --git a/src/structures/slash.ts b/src/structures/slash.ts new file mode 100644 index 0000000..c4ead4b --- /dev/null +++ b/src/structures/slash.ts @@ -0,0 +1,60 @@ +import { Client } from '../models/client.ts' +import { INTERACTION_CALLBACK } from '../types/endpoint.ts' +import { MemberPayload } from '../types/guild.ts' +import { + InteractionData, + InteractionPayload, + InteractionResponsePayload, + InteractionResponseType +} from '../types/slash.ts' +import { Embed } from './embed.ts' + +export interface InteractionResponse { + type?: InteractionResponseType + content?: string + embeds?: Embed[] + tts?: boolean + flags?: number +} + +export class Interaction { + client: Client + type: number + token: string + member: MemberPayload + id: string + data: InteractionData + + constructor(client: Client, data: InteractionPayload) { + this.client = client + this.type = data.type + this.token = data.token + this.member = data.member + this.id = data.id + this.data = data.data + } + + async respond(data: InteractionResponse): Promise { + const payload: InteractionResponsePayload = { + type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: + data.type === undefined || + data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE || + data.type === InteractionResponseType.CHANNEL_MESSAGE + ? { + content: data.content ?? '', + embeds: data.embeds, + tts: data.tts ?? false, + flags: data.flags ?? undefined + } + : undefined + } + + await this.client.rest.post( + INTERACTION_CALLBACK(this.id, this.token), + payload + ) + + return this + } +} diff --git a/src/test/slash-cmd.ts b/src/test/slash-cmd.ts new file mode 100644 index 0000000..043b96d --- /dev/null +++ b/src/test/slash-cmd.ts @@ -0,0 +1,51 @@ +import { TOKEN } from './config.ts' + +export const CMD = { + name: 'blep', + description: 'Send a random adorable animal photo', + options: [ + { + name: 'animal', + description: 'The type of animal', + type: 3, + required: true, + choices: [ + { + name: 'Dog', + value: 'animal_dog' + }, + { + name: 'Cat', + value: 'animal_dog' + }, + { + name: 'Penguin', + value: 'animal_penguin' + } + ] + }, + { + name: 'only_smol', + description: 'Whether to show only baby animals', + type: 5, + required: false + } + ] +} + +// fetch('https://discord.com/api/v8/applications/783937840752099332/commands', { +fetch( + 'https://discord.com/api/v8/applications/783937840752099332/guilds/783319033205751809/commands', + { + method: 'POST', + body: JSON.stringify(CMD), + headers: { + 'Content-Type': 'application/json', + Authorization: + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + 'Bot ' + TOKEN + } + } +) + .then((r) => r.json()) + .then(console.log) diff --git a/src/test/slash.ts b/src/test/slash.ts new file mode 100644 index 0000000..55e65d4 --- /dev/null +++ b/src/test/slash.ts @@ -0,0 +1,16 @@ +import { Client, Intents } from '../../mod.ts' +import { TOKEN } from './config.ts' + +const client = new Client() + +client.on('ready', () => { + console.log('Logged in!') +}) + +client.on('interactionCreate', async (d) => { + await d.respond({ + content: `Hi, ${d.member.user.username}!` + }) +}) + +client.connect(TOKEN, Intents.None) diff --git a/src/types/endpoint.ts b/src/types/endpoint.ts index a34575a..2271340 100644 --- a/src/types/endpoint.ts +++ b/src/types/endpoint.ts @@ -191,81 +191,35 @@ const VOICE_REGIONS = (guildID: string): string => const CLIENT_USER = (): string => `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me` -export default [ - GUILDS, - GUILD, - GUILD_AUDIT_LOGS, - GUILD_WIDGET, - GUILD_EMOJI, - GUILD_ROLE, - GUILD_ROLES, - GUILD_INTEGRATION, - GUILD_INTEGRATIONS, - GUILD_INTEGARTION_SYNC, - GUILD_WIDGET_IMAGE, - GUILD_BAN, - GUILD_BANS, - GUILD_CHANNEL, - GUILD_CHANNELS, - GUILD_MEMBER, - CLIENT_USER, - GUILD_MEMBERS, - GUILD_MEMBER_ROLE, - GUILD_INVITES, - GUILD_LEAVE, - GUILD_PRUNE, - GUILD_VANITY_URL, - GUILD_NICK, - GUILD_PREVIEW, - CHANNEL, - CHANNELS, - CHANNEL_MESSAGE, - CHANNEL_MESSAGES, - CHANNEL_CROSSPOST, - MESSAGE_REACTIONS, - MESSAGE_REACTION, - MESSAGE_REACTION_ME, - MESSAGE_REACTION_USER, - CHANNEL_BULK_DELETE, - CHANNEL_FOLLOW, - CHANNEL_INVITES, - CHANNEL_PIN, - CHANNEL_PINS, - CHANNEL_PERMISSION, - CHANNEL_TYPING, - GROUP_RECIPIENT, - CURRENT_USER, - CURRENT_USER_GUILDS, - USER_DM, - USER_CONNECTIONS, - LEAVE_GUILD, - USER, - CHANNEL_WEBHOOKS, - GUILD_WEBHOOK, - WEBHOOK, - WEBHOOK_WITH_TOKEN, - SLACK_WEBHOOK, - GITHUB_WEBHOOK, - GATEWAY, - GATEWAY_BOT, - CUSTOM_EMOJI, - GUILD_ICON, - GUILD_SPLASH, - GUILD_DISCOVERY_SPLASH, - GUILD_BANNER, - DEFAULT_USER_AVATAR, - USER_AVATAR, - APPLICATION_ASSET, - ACHIEVEMENT_ICON, - TEAM_ICON, - EMOJI, - GUILD_EMOJIS, - TEMPLATE, - INVITE, - VOICE_REGIONS -] +const APPLICATION_COMMANDS = (id: string): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands` + +const APPLICATION_COMMAND = (id: string, cmdID: string): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands/${cmdID}` + +const APPLICATION_GUILD_COMMANDS = (id: string, guildID: string): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands` + +const APPLICATION_GUILD_COMMAND = ( + id: string, + guildID: string, + cmdID: string +): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands/${cmdID}` + +const WEBHOOK_MESSAGE = (id: string, token: string, msgID: string): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/webhooks/${id}/${token}/messages/${msgID}` + +const INTERACTION_CALLBACK = (id: string, token: string): string => + `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/interactions/${id}/${token}/callback` export { + INTERACTION_CALLBACK, + APPLICATION_COMMAND, + APPLICATION_GUILD_COMMAND, + WEBHOOK_MESSAGE, + APPLICATION_COMMANDS, + APPLICATION_GUILD_COMMANDS, GUILDS, GUILD, GUILD_AUDIT_LOGS, diff --git a/src/types/gateway.ts b/src/types/gateway.ts index 5a1dd3e..bbd2e48 100644 --- a/src/types/gateway.ts +++ b/src/types/gateway.ts @@ -105,7 +105,8 @@ export enum GatewayEvents { User_Update = 'USER_UPDATE', Voice_Server_Update = 'VOICE_SERVER_UPDATE', Voice_State_Update = 'VOICE_STATE_UPDATE', - Webhooks_Update = 'WEBHOOKS_UPDATE' + Webhooks_Update = 'WEBHOOKS_UPDATE', + Interaction_Create = 'INTERACTION_CREATE' } export interface IdentityPayload { diff --git a/src/types/slash.ts b/src/types/slash.ts new file mode 100644 index 0000000..c296280 --- /dev/null +++ b/src/types/slash.ts @@ -0,0 +1,82 @@ +import { EmbedPayload } from './channel.ts' +import { MemberPayload } from './guild.ts' + +export interface InteractionOption { + name: string + value?: any + options?: any[] +} + +export interface InteractionData { + options: InteractionOption[] + name: string + id: string +} + +export enum InteractionType { + PING = 1, + APPLICATION_COMMAND = 2 +} + +export interface InteractionPayload { + type: InteractionType + token: string + member: MemberPayload + id: string + data: InteractionData +} + +export interface SlashCommandChoice { + name: string + value: string +} + +export enum SlashCommandOptionType { + SUB_COMMAND = 1, + SUB_COMMAND_GROUP = 2, + STRING = 3, + INTEGER = 4, + BOOLEAN = 5, + USER = 6, + CHANNEL = 7, + ROLE = 8 +} + +export interface SlashCommandOption { + name: string + description: string + type: SlashCommandOptionType + required: boolean + choices?: SlashCommandChoice[] +} + +export interface SlashCommandPayload { + name: string + description: string + options: SlashCommandOption[] +} + +export enum InteractionResponseType { + PONG = 1, + ACKNOWLEDGE = 2, + CHANNEL_MESSAGE = 3, + CHANNEL_MESSAGE_WITH_SOURCE = 4, + ACK_WITH_SOURCE = 5 +} + +export interface InteractionResponsePayload { + type: InteractionResponseType + data?: InteractionResponseDataPayload +} + +export interface InteractionResponseDataPayload { + tts?: boolean + content: string + embeds?: EmbedPayload[] + allowed_mentions?: { + parse?: 'everyone' | 'users' | 'roles' + roles?: string[] + users?: string[] + } + flags?: number +}