From a2b53ab51e1a721c8e0692743ad6ca76109f7c23 Mon Sep 17 00:00:00 2001 From: Yuzu Date: Sat, 2 Jul 2022 10:19:08 -0500 Subject: [PATCH] fix: TextChannel no longer extends GuildChannel --- structures/BaseChannel.ts | 2 +- structures/DMChannel.ts | 11 ++++---- structures/GuildChannel.ts | 51 +++++++++++++++++++++++++++++++++++- structures/NewsChannel.ts | 9 ++++--- structures/TextChannel.ts | 43 +++++++++--------------------- structures/Webhook.ts | 47 +++++++++++++++++++++++++++++++++ structures/WelcomeChannel.ts | 1 + util/Routes.ts | 4 +++ 8 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 structures/Webhook.ts diff --git a/structures/BaseChannel.ts b/structures/BaseChannel.ts index f7814ae..7a39ab9 100644 --- a/structures/BaseChannel.ts +++ b/structures/BaseChannel.ts @@ -62,7 +62,7 @@ export abstract class BaseChannel implements Model { return new VoiceChannel(session, channel, channel.guild_id!); default: if (textBasedChannels.includes(channel.type)) { - return new TextChannel(session, channel, channel.guild_id!); + return new TextChannel(session, channel); } throw new Error("Channel was not implemented"); } diff --git a/structures/DMChannel.ts b/structures/DMChannel.ts index d13bd5d..1045c67 100644 --- a/structures/DMChannel.ts +++ b/structures/DMChannel.ts @@ -1,24 +1,23 @@ import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; -import type { DiscordChannel } from "../vendor/external.ts"; -import BaseChannel from "./BaseChannel.ts"; +import type { ChannelTypes, DiscordChannel } from "../vendor/external.ts"; +import TextChannel from "./TextChannel.ts"; import User from "./User.ts"; import * as Routes from "../util/Routes.ts"; -export class DMChannel extends BaseChannel implements Model { +export class DMChannel extends TextChannel implements Model { constructor(session: Session, data: DiscordChannel) { super(session, data); this.user = new User(this.session, data.recipents!.find((r) => r.id !== this.session.botId)!); - + this.type = data.type as ChannelTypes.DM | ChannelTypes.GroupDm; if (data.last_message_id) { this.lastMessageId = data.last_message_id; } } + override type: ChannelTypes.DM | ChannelTypes.GroupDm; user: User; - lastMessageId?: Snowflake; async close() { const channel = await this.session.rest.runMethod( diff --git a/structures/GuildChannel.ts b/structures/GuildChannel.ts index 2854425..58065bc 100644 --- a/structures/GuildChannel.ts +++ b/structures/GuildChannel.ts @@ -1,20 +1,44 @@ import type { Model } from "./Base.ts"; import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; -import type { DiscordChannel, DiscordInviteMetadata } from "../vendor/external.ts"; +import type { ChannelTypes, DiscordChannel, DiscordWebhook, DiscordInviteMetadata } from "../vendor/external.ts"; +import { urlToBase64 } from "../util/urlToBase64.ts"; import BaseChannel from "./BaseChannel.ts"; import Invite from "./Invite.ts"; +import Webhook from "./Webhook.ts"; +import ThreadChannel from "./ThreadChannel.ts"; import * as Routes from "../util/Routes.ts"; +export interface CreateWebhook { + name: string; + avatar?: string; + reason?: string; +} + + +/** + * Represent the options object to create a Thread Channel + * @link https://discord.com/developers/docs/resources/channel#start-thread-without-message + */ +export interface ThreadCreateOptions { + name: string; + autoArchiveDuration: 60 | 1440 | 4320 | 10080; + type: 10 | 11 | 12; + invitable?: boolean; + reason?: string; +} + export class GuildChannel extends BaseChannel implements Model { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { super(session, data); + this.type = data.type as number this.guildId = guildId; this.position = data.position; data.topic ? this.topic = data.topic : null; data.parent_id ? this.parentId = data.parent_id : undefined; } + override type: Exclude; guildId: Snowflake; topic?: string; position?: number; @@ -40,6 +64,31 @@ export class GuildChannel extends BaseChannel implements Model { }, ); } + + async createThread(options: ThreadCreateOptions): Promise { + const thread = await this.session.rest.runMethod( + this.session.rest, + "POST", + Routes.CHANNEL_CREATE_THREAD(this.id), + options, + ); + return new ThreadChannel(this.session, thread, this.guildId); + } + + async createWebhook(options: CreateWebhook) { + const webhook = await this.session.rest.runMethod( + this.session.rest, + "POST", + Routes.CHANNEL_WEBHOOKS(this.id), + { + name: options.name, + avatar: options.avatar ? urlToBase64(options.avatar) : undefined, + reason: options.reason, + } + ); + + return new Webhook(this.session, webhook); + } } export default GuildChannel; diff --git a/structures/NewsChannel.ts b/structures/NewsChannel.ts index ef76049..e0b7dad 100644 --- a/structures/NewsChannel.ts +++ b/structures/NewsChannel.ts @@ -1,14 +1,17 @@ import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; -import type { DiscordChannel } from "../vendor/external.ts"; -import TextChannel from "./TextChannel.ts"; +import type { ChannelTypes, DiscordChannel } from "../vendor/external.ts"; +import GuildChannel from "./GuildChannel.ts"; import Message from "./Message.ts"; -export class NewsChannel extends TextChannel { +export class NewsChannel extends GuildChannel { constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { super(session, data, guildId); + this.type = data.type as ChannelTypes.GuildNews; this.defaultAutoArchiveDuration = data.default_auto_archive_duration; } + + override type: ChannelTypes.GuildNews; defaultAutoArchiveDuration?: number; crosspostMessage(messageId: Snowflake): Promise { diff --git a/structures/TextChannel.ts b/structures/TextChannel.ts index 2491391..c2ea25f 100644 --- a/structures/TextChannel.ts +++ b/structures/TextChannel.ts @@ -4,8 +4,7 @@ import type { GetMessagesOptions, GetReactions } from "../util/Routes.ts"; import type { DiscordChannel, DiscordInvite, DiscordMessage, TargetTypes } from "../vendor/external.ts"; import type { CreateMessage, EditMessage, ReactionResolvable } from "./Message.ts"; import { ChannelTypes } from "../vendor/external.ts"; -import GuildChannel from "./GuildChannel.ts"; -import ThreadChannel from "./ThreadChannel.ts"; +import BaseChannel from "./BaseChannel.ts"; import Message from "./Message.ts"; import Invite from "./Invite.ts"; import * as Routes from "../util/Routes.ts"; @@ -25,18 +24,6 @@ export interface DiscordInviteOptions { targetApplicationId?: Snowflake; } -/** - * Represent the options object to create a Thread Channel - * @link https://discord.com/developers/docs/resources/channel#start-thread-without-message - */ -export interface ThreadCreateOptions { - name: string; - autoArchiveDuration: 60 | 1440 | 4320 | 10080; - type: 10 | 11 | 12; - invitable?: boolean; - reason?: string; -} - export const textBasedChannels = [ ChannelTypes.DM, ChannelTypes.GroupDm, @@ -54,14 +41,20 @@ export type TextBasedChannels = | ChannelTypes.GuildNews | ChannelTypes.GuildText; -export class TextChannel extends GuildChannel { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - data.last_message_id ? this.lastMessageId = data.last_message_id : undefined; - data.last_pin_timestamp ? this.lastPinTimestamp = data.last_pin_timestamp : undefined; - this.type = data.type as TextBasedChannels; +export class TextChannel extends BaseChannel { + constructor(session: Session, data: DiscordChannel) { + super(session, data); + this.type = data.type as number; this.rateLimitPerUser = data.rate_limit_per_user ?? 0; this.nsfw = !!data.nsfw ?? false; + + if (data.last_message_id) { + this.lastMessageId = data.last_message_id; + } + + if (data.last_pin_timestamp) { + this.lastPinTimestamp = data.last_pin_timestamp; + } } override type: TextBasedChannels; @@ -100,16 +93,6 @@ export class TextChannel extends GuildChannel { return new Invite(this.session, invite); } - async createThread(options: ThreadCreateOptions): Promise { - const thread = await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.CHANNEL_CREATE_THREAD(this.id), - options, - ); - return new ThreadChannel(this.session, thread, this.guildId); - } - async fetchMessages(options?: GetMessagesOptions): Promise { if (options?.limit! > 100) throw Error("Values must be between 0-100"); const messages = await this.session.rest.runMethod( diff --git a/structures/Webhook.ts b/structures/Webhook.ts new file mode 100644 index 0000000..6a85a22 --- /dev/null +++ b/structures/Webhook.ts @@ -0,0 +1,47 @@ +import type { Model } from "./Base.ts"; +import type { Session } from "../session/Session.ts"; +import type { Snowflake } from "../util/Snowflake.ts"; +import type { DiscordWebhook, WebhookTypes } from "../vendor/external.ts"; +import { iconHashToBigInt } from "../util/hash.ts"; +import User from "./User.ts"; + +export class Webhook implements Model { + constructor(session: Session, data: DiscordWebhook) { + this.session = session; + this.id = data.id; + this.type = data.type; + this.token = data.token; + + if (data.avatar) { + this.avatar = iconHashToBigInt(data.avatar); + } + + if (data.user) { + this.user = new User(session, data.user); + } + + if (data.guild_id) { + this.guildId = data.guild_id; + } + + if (data.channel_id) { + this.channelId = data.channel_id; + } + + if (data.application_id) { + this.applicationId = data.application_id; + } + } + + readonly session: Session; + readonly id: Snowflake; + type: WebhookTypes; + token?: string; + avatar?: bigint; + applicationId?: Snowflake; + channelId?: Snowflake; + guildId?: Snowflake; + user?: User; +} + +export default Webhook; diff --git a/structures/WelcomeChannel.ts b/structures/WelcomeChannel.ts index e540207..b1bcb5d 100644 --- a/structures/WelcomeChannel.ts +++ b/structures/WelcomeChannel.ts @@ -5,6 +5,7 @@ import type { DiscordWelcomeScreenChannel } from "../vendor/external.ts"; import Emoji from "./Emoji.ts"; /** + * Not a channel * @link https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure */ export class WelcomeChannel implements Model { diff --git a/util/Routes.ts b/util/Routes.ts index db2654e..247cadd 100644 --- a/util/Routes.ts +++ b/util/Routes.ts @@ -224,3 +224,7 @@ export function CHANNEL_MESSAGE_CROSSPOST(channelId: Snowflake, messageId: Snowf export function GUILD_MEMBER_ROLE(guildId: Snowflake, memberId: Snowflake, roleId: Snowflake) { return `/guilds/${guildId}/members/${memberId}/roles/${roleId}`; } + +export function CHANNEL_WEBHOOKS(channelId: Snowflake) { + return `/channels/${channelId}/webhooks`; +}