From 21b4b4643bcb84d5c8e4c6abe54b5e7f296cffa4 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Sat, 3 Jun 2023 18:56:43 +0100 Subject: [PATCH] feat: implement channel webhook collection --- .eslintrc.json | 2 +- src/Client.ts | 3 + src/classes/Channel.ts | 17 ++++ src/classes/ChannelWebhook.ts | 94 +++++++++++++++++++++ src/collections/ChannelWebhookCollection.ts | 55 ++++++++++++ src/collections/index.ts | 1 + src/hydration/channelWebhook.ts | 34 ++++++++ src/hydration/index.ts | 3 + 8 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/classes/ChannelWebhook.ts create mode 100644 src/collections/ChannelWebhookCollection.ts create mode 100644 src/hydration/channelWebhook.ts diff --git a/.eslintrc.json b/.eslintrc.json index 9f9b0a51..589d74e4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,7 +17,7 @@ "strings": false, "identifiers": false, "templates": false, - "skipWords": ["uri", "webhook", "unreads"], + "skipWords": ["uri", "webhook", "webhooks", "unreads"], "minLength": 3 } ], diff --git a/src/Client.ts b/src/Client.ts index bd17c5ce..7f8d40fe 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -9,6 +9,7 @@ import { BotCollection, ChannelCollection, ChannelUnreadCollection, + ChannelWebhookCollection, EmojiCollection, MessageCollection, ServerCollection, @@ -148,6 +149,7 @@ export class Client extends EventEmitter { readonly bots; readonly channels; readonly channelUnreads; + readonly channelWebhooks; readonly emojis; readonly messages; readonly servers; @@ -219,6 +221,7 @@ export class Client extends EventEmitter { this.bots = new BotCollection(this); this.channels = new ChannelCollection(this); this.channelUnreads = new ChannelUnreadCollection(this); + this.channelWebhooks = new ChannelWebhookCollection(this); this.emojis = new EmojiCollection(this); this.messages = new MessageCollection(this); this.servers = new ServerCollection(this); diff --git a/src/classes/Channel.ts b/src/classes/Channel.ts index 792b130b..8f1178e8 100644 --- a/src/classes/Channel.ts +++ b/src/classes/Channel.ts @@ -390,6 +390,23 @@ export class Channel { ); } + /** + * Fetch a channel's webhooks + * @requires `TextChannel`, `Group` + * @returns Webhooks + */ + async fetchWebhooks() { + const webhooks = await this.#collection.client.api.get( + `/channels/${this.id as ""}/webhooks` + ); + + return batch(() => + webhooks.map((webhook) => + this.#collection.client.channelWebhooks.getOrCreate(webhook.id, webhook) + ) + ); + } + /** * Edit a channel * @param data Changes diff --git a/src/classes/ChannelWebhook.ts b/src/classes/ChannelWebhook.ts new file mode 100644 index 00000000..ab88c974 --- /dev/null +++ b/src/classes/ChannelWebhook.ts @@ -0,0 +1,94 @@ +import { DataEditWebhook } from "revolt-api"; + +import { ChannelWebhookCollection } from "../collections"; +import { hydrate } from "../hydration"; + +/** + * Channel Webhook Class + */ +export class ChannelWebhook { + readonly #collection: ChannelWebhookCollection; + readonly id: string; + + /** + * Construct Channel Webhook + * @param collection Collection + * @param id Webhook + */ + constructor(collection: ChannelWebhookCollection, id: string) { + this.#collection = collection; + this.id = id; + } + + /** + * Webhook name + */ + get name() { + return this.#collection.getUnderlyingObject(this.id).name; + } + + /** + * Webhook avatar + */ + get avatar() { + return this.#collection.getUnderlyingObject(this.id).avatar; + } + + /** + * Webhook avatar URL + */ + get avatarURL() { + return this.#collection + .getUnderlyingObject(this.id) + .avatar?.createFileURL({ max_side: 256 }); + } + + /** + * Channel ID this webhook belongs to + */ + get channelId() { + return this.#collection.getUnderlyingObject(this.id).channelId; + } + + /** + * Channel this webhook belongs to + */ + get channel() { + return this.#collection.client.channels.get( + this.#collection.getUnderlyingObject(this.id).channelId + ); + } + + /** + * Secret token for sending messages to this webhook + */ + get token() { + return this.#collection.getUnderlyingObject(this.id).token; + } + + /** + * Edit this webhook + */ + async edit(data: DataEditWebhook) { + const webhook = await this.#collection.client.api.patch( + `/webhooks/${this.id as ""}/${this.token as ""}`, + data + ); + + this.#collection.updateUnderlyingObject( + this.id, + hydrate("channelWebhook", webhook, this.#collection.client) + ); + } + + /** + * Delete this webhook + */ + async delete() { + await this.#collection.client.api.delete( + `/webhooks/${this.id}/${this.token}` + ); + + this.#collection.delete(this.id); + } +} diff --git a/src/collections/ChannelWebhookCollection.ts b/src/collections/ChannelWebhookCollection.ts new file mode 100644 index 00000000..c2b72be5 --- /dev/null +++ b/src/collections/ChannelWebhookCollection.ts @@ -0,0 +1,55 @@ +import { API } from ".."; +import { ChannelWebhook } from "../classes/ChannelWebhook"; +import { HydratedChannelWebhook } from "../hydration/channelWebhook"; + +import { ClassCollection } from "."; + +/** + * Collection of Channel Webhooks + */ +export class ChannelWebhookCollection extends ClassCollection< + ChannelWebhook, + HydratedChannelWebhook +> { + /** + * Fetch webhook by ID + * @param id Id + * @returns Webhook + */ + async fetch(id: string): Promise { + const webhook = this.get(id); + if (webhook) return webhook; + const data = await this.client.api.get(`/webhooks/${id as ""}`); + return this.getOrCreate(data.id, data as API.Webhook); + } + + /** + * Create webhook with ID and token + * @param id Id + * @param token Token + * @returns Webhook + */ + async fromToken(id: string, token: string): Promise { + const webhook = this.get(id); + if (webhook) return webhook; + const data = await this.client.api.get( + `/webhooks/${id as ""}/${token as ""}` + ); + return this.getOrCreate(data.id, data); + } + + /** + * Get or create + * @param id Id + * @param data Data + */ + getOrCreate(id: string, data: API.Webhook) { + if (this.has(id)) { + return this.get(id)!; + } else { + const instance = new ChannelWebhook(this, id); + this.create(id, "channelWebhook", instance, this.client, data); + return instance; + } + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index 310f9584..fca9681c 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -3,6 +3,7 @@ export * from "./Collection"; export { BotCollection } from "./BotCollection"; export { ChannelCollection } from "./ChannelCollection"; export { ChannelUnreadCollection } from "./ChannelUnreadCollection"; +export { ChannelWebhookCollection } from "./ChannelWebhookCollection"; export { EmojiCollection } from "./EmojiCollection"; export { MessageCollection } from "./MessageCollection"; export { ServerCollection } from "./ServerCollection"; diff --git a/src/hydration/channelWebhook.ts b/src/hydration/channelWebhook.ts new file mode 100644 index 00000000..3d2b5726 --- /dev/null +++ b/src/hydration/channelWebhook.ts @@ -0,0 +1,34 @@ +import { API, Client, File } from ".."; +import type { Merge } from "../lib/merge"; + +import { Hydrate } from "."; + +export type HydratedChannelWebhook = { + id: string; + name: string; + avatar?: File; + channelId: string; + token: string; +}; + +export const channelWebhookHydration: Hydrate< + Merge, + HydratedChannelWebhook +> = { + keyMapping: { + id: "id", + name: "name", + avatar: "avatar", + channel_id: "channelId", + token: "token", + }, + functions: { + id: (webhook) => webhook.id, + name: (webhook) => webhook.name, + avatar: (webhook, ctx) => + webhook.avatar ? new File(ctx as Client, webhook.avatar) : undefined, + channelId: (webhook) => webhook.channel_id, + token: (webhook) => webhook.token!, + }, + initialHydration: () => ({}), +}; diff --git a/src/hydration/index.ts b/src/hydration/index.ts index 828c632a..a2003594 100644 --- a/src/hydration/index.ts +++ b/src/hydration/index.ts @@ -1,6 +1,7 @@ import { botHydration } from "./bot"; import { channelHydration } from "./channel"; import { channelUnreadHydration } from "./channelUnread"; +import { channelWebhookHydration } from "./channelWebhook"; import { emojiHydration } from "./emoji"; import { messageHydration } from "./message"; import { serverHydration } from "./server"; @@ -15,6 +16,7 @@ export { UserBadges, UserFlags } from "./user"; export type { HydratedBot } from "./bot"; export type { HydratedChannel } from "./channel"; export type { HydratedChannelUnread } from "./channelUnread"; +export type { HydratedChannelWebhook } from "./channelWebhook"; export type { HydratedEmoji } from "./emoji"; export type { HydratedMessage } from "./message"; export type { HydratedServer } from "./server"; @@ -77,6 +79,7 @@ const hydrators = { bot: botHydration, channel: channelHydration, channelUnread: channelUnreadHydration, + channelWebhook: channelWebhookHydration, emoji: emojiHydration, message: messageHydration, server: serverHydration,