From 90d299679a3b05b9e91de4c643c575805db9f46d Mon Sep 17 00:00:00 2001 From: Tristan Camejo Date: Thu, 21 Apr 2022 10:46:28 +1000 Subject: [PATCH 1/3] fix: optional body --- src/client/rest/REST.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/rest/REST.ts b/src/client/rest/REST.ts index a7e4750..cc930f1 100644 --- a/src/client/rest/REST.ts +++ b/src/client/rest/REST.ts @@ -6,23 +6,23 @@ export class REST { public API_URL = 'https://discord.com/api/v10'; public constructor(private client: Client) {} - public async get(path: string, body: any) { + public async get(path: string, body?: any) { return this.request(path, body, RequestMethod.Get); } - public async delete(path: string, body: any) { + public async delete(path: string, body?: any) { return this.request(path, body, RequestMethod.Delete); } - public async post(path: string, body: any) { + public async post(path: string, body?: any) { return this.request(path, body, RequestMethod.Post); } - public async put(path: string, body: any) { + public async put(path: string, body?: any) { return this.request(path, body, RequestMethod.Put); } - public async patch(path: string, body: any) { + public async patch(path: string, body?: any) { return this.request(path, body, RequestMethod.Patch); } From 1d4939bd7553f7a00adeb5d84a3f81f52838d832 Mon Sep 17 00:00:00 2001 From: Tristan Camejo Date: Thu, 21 Apr 2022 10:49:35 +1000 Subject: [PATCH 2/3] feat: Interaction --- src/client/ws/Gateway.ts | 9 ++++++ src/structures/Interaction.ts | 59 +++++++++++++++++++++++++++++++++++ src/tests/index.ts | 21 ++++++++++++- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/structures/Interaction.ts diff --git a/src/client/ws/Gateway.ts b/src/client/ws/Gateway.ts index cf0788c..61a7ad4 100644 --- a/src/client/ws/Gateway.ts +++ b/src/client/ws/Gateway.ts @@ -1,4 +1,5 @@ import { + APIInteraction, APIMessage, APIUnavailableGuild, ChannelType, @@ -9,6 +10,7 @@ import { } from 'discord-api-types/v10'; import { WebSocket } from 'ws'; import { Channel, Guild, Message } from '../../structures'; +import { Interaction } from '../../structures/Interaction'; import { log } from '../../utils/logger'; import type { Client } from '../Client'; @@ -115,6 +117,13 @@ export class Gateway { this.client.emit('guildDelete', guild ?? null); break; } + case 'INTERACTION_CREATE': { + const apiInteraction = buffer.d as APIInteraction; + + const interaction = new Interaction(apiInteraction, this.client); + this.client.emit('interaction', interaction); + break; + } case 'MESSAGE_CREATE': { const apiMessage = buffer.d as APIMessage; diff --git a/src/structures/Interaction.ts b/src/structures/Interaction.ts new file mode 100644 index 0000000..9cb716f --- /dev/null +++ b/src/structures/Interaction.ts @@ -0,0 +1,59 @@ +import { log } from 'console'; +import { APIInteraction, APIMessage, RESTPostAPIChannelMessageJSONBody, Routes, Snowflake } from 'discord-api-types/v10'; +import type { Client } from '../client'; +import { Base } from './Base'; +import type { Channel } from './Channel'; +import type { Guild } from './Guild'; +import { GuildMember } from './GuildMember'; +import { Message } from './Message'; +import { User } from './User'; +export class Interaction extends Base { + public readonly id: Snowflake = this.raw.id; + + public readonly guild?: Guild = this.raw.guild_id ? this.client.guilds.get(this.raw.guild_id) : undefined; + public readonly channel?: Channel = this.raw.channel_id ? this.client.channels.get(this.raw.channel_id) : undefined; + + public readonly user?: User = this.raw.user ? new User(this.raw.user) : undefined; + public readonly member?: GuildMember = this.guild + ? this.user + ? this.raw.member + ? new GuildMember(this.raw.member, this.user, this.guild, this.client) + : undefined + : undefined + : undefined; + + private readonly token: string = this.raw.token; + + public constructor(private raw: APIInteraction, client: Client) { + super(client); + } + + public async reply(payload: Omit) { + if (!this.client.user) throw new Error('Client user not set'); + + const res = await this.client.rest.post( + Routes.interactionCallback(this.id, this.token), + JSON.stringify({ + type: 4, + data: { + ...payload + } + }) + ); + + if (res.ok) { + const res = await this.client.rest.get(Routes.webhookMessage(this.client.user.id, this.token)); + + if (!res.ok) throw new Error(`Failed to get original message: ${res.status} ${res.statusText}`); + + const apiMessage = (await res.json()) as APIMessage; + + return new Message(apiMessage, this.client); + } + + const json = await res.json(); + log({ state: 'DEBUG', json }); + + throw new Error(`${res.status} ${res.statusText}`); + } +} diff --git a/src/tests/index.ts b/src/tests/index.ts index 80c5d50..7c4e2f8 100644 --- a/src/tests/index.ts +++ b/src/tests/index.ts @@ -1,6 +1,7 @@ import 'dotenv/config'; -import { Client, Message, User, Channel } from '..'; +import { Channel, Client, Message, User } from '..'; import type { Guild } from '../structures'; +import type { Interaction } from '../structures/Interaction'; const client = new Client({ intents: 131071 // All intents @@ -28,6 +29,24 @@ client.on('message', (message: Message) => { }); }); +client.on('interaction', (interaction: Interaction) => { + void interaction + .reply({ + content: 'pong!', + embeds: [ + { + description: 'pong!' + } + ] + }) + .then((m) => { + void m.reply({ + content: 'hey i replied to my own interaction :woosh:' + }); + }) + .catch(console.error); +}); + client.on('channelCreate', (channel: Channel) => { console.log(`A new channel called ${channel.name} was created!`); }); From 461caf1a6287b9c34b86de952d74fcb2e51be3cd Mon Sep 17 00:00:00 2001 From: tristan <69066026+twisttaan@users.noreply.github.com> Date: Thu, 21 Apr 2022 10:44:14 +1000 Subject: [PATCH 3/3] feat: client.user (#32) --- src/client/Client.ts | 4 +++- src/client/ws/Gateway.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client/Client.ts b/src/client/Client.ts index d63d690..e9563f8 100644 --- a/src/client/Client.ts +++ b/src/client/Client.ts @@ -1,6 +1,6 @@ import { env } from 'node:process'; import { EventEmitter } from 'stream'; -import type { Channel, Guild } from '../structures'; +import type { Channel, Guild, User } from '../structures'; import type { ClientOptions } from '../types/lib'; import { REST } from './rest/REST'; import { Gateway } from './ws/Gateway'; @@ -13,6 +13,8 @@ export class Client extends EventEmitter { public readonly channels = new Map(); public intents: number; + public user?: User; + public constructor(options: ClientOptions) { super(); this.intents = options.intents || 0; diff --git a/src/client/ws/Gateway.ts b/src/client/ws/Gateway.ts index 61a7ad4..fa5d995 100644 --- a/src/client/ws/Gateway.ts +++ b/src/client/ws/Gateway.ts @@ -9,7 +9,7 @@ import { GatewayReadyDispatch } from 'discord-api-types/v10'; import { WebSocket } from 'ws'; -import { Channel, Guild, Message } from '../../structures'; +import { Channel, Guild, Message, User } from '../../structures'; import { Interaction } from '../../structures/Interaction'; import { log } from '../../utils/logger'; import type { Client } from '../Client'; @@ -70,6 +70,7 @@ export class Gateway { case 'READY': { const readyPayload = buffer as GatewayReadyDispatch; this.readyGuilds = readyPayload.d.guilds; + this.client.user = new User(readyPayload.d.user); break; }