From 066434f331bb9d080ab029d2a1d181308c2a3d20 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Wed, 28 Sep 2022 23:26:56 +0900 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20feat:=20add=20cha?= =?UTF-8?q?nnel=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/structures/guildForunChannel.ts | 21 ++++++++++++++++ src/types/channel.ts | 38 ++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/structures/guildForunChannel.ts diff --git a/src/structures/guildForunChannel.ts b/src/structures/guildForunChannel.ts new file mode 100644 index 00000000..dff49294 --- /dev/null +++ b/src/structures/guildForunChannel.ts @@ -0,0 +1,21 @@ +import { GuildForumTagPayload } from '../../mod.ts' + +export class GuildForumTag { + id!: string + name!: string + moderated!: boolean + emojiID!: string + emojiName!: string | null + + constructor(data: GuildForumTagPayload) { + this.readFromData(data) + } + + readFromData(data: GuildForumTagPayload) { + this.id = data.id + this.name = data.name + this.moderated = data.moderated + this.emojiID = data.emoji_id + this.emojiName = data.emoji_name + } +} diff --git a/src/types/channel.ts b/src/types/channel.ts index eb7620c4..d09b8654 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -37,6 +37,11 @@ export interface GuildTextBasedChannelPayload topic?: string } +export enum ChannelFlags { + PINNED = 1 << 1, + REQUIRE_TAG = 1 << 4 +} + export interface ThreadChannelPayload extends TextChannelPayload, GuildChannelPayload { @@ -46,6 +51,9 @@ export interface ThreadChannelPayload thread_metadata: ThreadMetadataPayload rate_limit_per_user: number owner_id: string + total_message_sent: number + applied_tags?: string[] + flags: number } export interface GuildTextChannelPayload extends GuildTextBasedChannelPayload { @@ -63,6 +71,32 @@ export interface GuildVoiceChannelPayload extends GuildChannelPayload { export interface GuildStageChannelPayload extends Omit {} +export interface GuildForumTagPayload { + id: string + name: string + moderated: boolean + emoji_id: string + emoji_name: string | null +} + +export interface GuildForumDefaultReactionPayload { + emoji_id: string | null + emoji_name: string | null +} + +export enum GuildForumSortOrderTypes { + LATEST_ACTIVITY = 0, + CREATION_DATE = 1 +} + +export interface GuildForumChannelPayload extends GuildChannelPayload { + default_thread_rate_limit_per_user: number + rate_limit_per_user: number + available_tags: GuildForumTagPayload[] + default_reaction_emoji: GuildForumDefaultReactionPayload | null + default_sort_order: GuildForumSortOrderTypes | null +} + export interface DMChannelPayload extends TextChannelPayload { recipients: UserPayload[] } @@ -194,7 +228,9 @@ export enum ChannelTypes { NEWS_THREAD = 10, PUBLIC_THREAD = 11, PRIVATE_THREAD = 12, - GUILD_STAGE_VOICE = 13 + GUILD_STAGE_VOICE = 13, + GUILD_DIRECTORY = 14, + GUILD_FORUM = 15 } export interface MessagePayload { From 3e5c7fb0f42a30bb967b385e441131625a36b2e8 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Mon, 3 Oct 2022 13:38:47 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=A9=B9=20fix:=20EmojisManager.fetch?= =?UTF-8?q?=20takes=20two=20arguments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/managers/emojis.ts | 17 +++++++++++++++++ src/structures/guildForunChannel.ts | 21 --------------------- 2 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 src/structures/guildForunChannel.ts diff --git a/src/managers/emojis.ts b/src/managers/emojis.ts index f99f9e12..9d2a6d11 100644 --- a/src/managers/emojis.ts +++ b/src/managers/emojis.ts @@ -1,3 +1,4 @@ +import type { Guild } from '../../mod.ts' import type { Client } from '../client/mod.ts' import { Emoji } from '../structures/emoji.ts' import type { EmojiPayload } from '../types/emoji.ts' @@ -32,4 +33,20 @@ export class EmojisManager extends BaseManager { .catch((e) => reject(e)) }) } + + /** Try to get Emoji from cache, if not found then fetch */ + async resolve( + key: string, + guild?: string | Guild + ): Promise { + const cacheValue = await this.get(key) + if (cacheValue !== undefined) return cacheValue + else { + if (guild !== undefined) { + const guildID = typeof guild === 'string' ? guild : guild.id + const fetchValue = await this.fetch(guildID, key).catch(() => undefined) + if (fetchValue !== undefined) return fetchValue + } + } + } } diff --git a/src/structures/guildForunChannel.ts b/src/structures/guildForunChannel.ts deleted file mode 100644 index dff49294..00000000 --- a/src/structures/guildForunChannel.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { GuildForumTagPayload } from '../../mod.ts' - -export class GuildForumTag { - id!: string - name!: string - moderated!: boolean - emojiID!: string - emojiName!: string | null - - constructor(data: GuildForumTagPayload) { - this.readFromData(data) - } - - readFromData(data: GuildForumTagPayload) { - this.id = data.id - this.name = data.name - this.moderated = data.moderated - this.emojiID = data.emoji_id - this.emojiName = data.emoji_name - } -} From 77052d817b2dd3911a1504aac96ca3f4b408f27f Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Mon, 3 Oct 2022 13:40:07 +0900 Subject: [PATCH 03/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20fix:=20flags=20pr?= =?UTF-8?q?operty=20is=20available=20everywhere?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit idk why but okay --- src/structures/channel.ts | 2 ++ src/types/channel.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/structures/channel.ts b/src/structures/channel.ts index fcb34850..071b3355 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -46,6 +46,7 @@ import type { ThreadChannel } from '../structures/threadChannel.ts' export class Channel extends SnowflakeBase { type!: ChannelTypes + flags!: number static cacheName = 'channel' @@ -61,6 +62,7 @@ export class Channel extends SnowflakeBase { readFromData(data: ChannelPayload): void { this.type = data.type ?? this.type this.id = data.id ?? this.id + this.flags = data.flags ?? this.flags } isDM(): this is DMChannel { diff --git a/src/types/channel.ts b/src/types/channel.ts index d09b8654..7a792a20 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -15,6 +15,7 @@ import type { export interface ChannelPayload { id: string type: ChannelTypes + flags: number } export interface TextChannelPayload extends ChannelPayload { @@ -53,7 +54,6 @@ export interface ThreadChannelPayload owner_id: string total_message_sent: number applied_tags?: string[] - flags: number } export interface GuildTextChannelPayload extends GuildTextBasedChannelPayload { From 52bd0e59fbb597ff36f2a42731c8b376477dd9a8 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 09:42:35 +0900 Subject: [PATCH 04/15] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20guild=20forum?= =?UTF-8?q?=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/structures/guildForumChannel.ts | 97 +++++++++++++++++++++++++++++ src/types/channel.ts | 26 +++++++- 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/structures/guildForumChannel.ts diff --git a/src/structures/guildForumChannel.ts b/src/structures/guildForumChannel.ts new file mode 100644 index 00000000..be89b662 --- /dev/null +++ b/src/structures/guildForumChannel.ts @@ -0,0 +1,97 @@ +import { + Emoji, + GuildForumChannelPayload, + GuildForumSortOrderTypes, + GuildForumTagPayload, + GuildTextChannel, + ModifyGuildForumChannelOption, + ModifyGuildForumChannelPayload +} from '../../mod.ts' +import { CHANNEL } from '../types/endpoint.ts' + +export class GuildForumTag { + id!: string + name!: string + moderated!: boolean + emojiID!: string + emojiName!: string | null + + constructor(data: GuildForumTagPayload) { + this.readFromData(data) + } + + readFromData(data: GuildForumTagPayload) { + this.id = data.id ?? this.id + this.name = data.name ?? this.name + this.moderated = data.moderated ?? this.moderated + this.emojiID = data.emoji_id ?? this.emojiID + this.emojiName = data.emoji_name ?? this.emojiName + } +} + +export class GuildForumChannel extends GuildTextChannel { + availableTags!: GuildForumTag[] + defaultReactionEmoji!: Emoji + defaultSortOrder!: GuildForumSortOrderTypes + + readFromData(data: GuildForumChannelPayload): void { + super.readFromData(data) + this.slowmode = data.rate_limit_per_user ?? this.slowmode + this.availableTags = + data.available_tags?.map((tag) => new GuildForumTag(tag)) ?? + this.availableTags + this.defaultReactionEmoji = data.default_reaction_emoji + ? new Emoji(this.client, { + id: data.default_reaction_emoji.emoji_id, + name: data.default_reaction_emoji.emoji_name + }) + : this.defaultReactionEmoji + this.defaultSortOrder = data.default_sort_order ?? this.defaultSortOrder + } + + async edit( + options?: ModifyGuildForumChannelOption + ): Promise { + if (options?.defaultReactionEmoji !== undefined) { + if (options.defaultReactionEmoji instanceof Emoji) { + options.defaultReactionEmoji = { + emoji_id: options.defaultReactionEmoji.id, + emoji_name: options.defaultReactionEmoji.name + } + } + } + if (options?.availableTags !== undefined) { + options.availableTags = options.availableTags?.map((tag) => { + if (tag instanceof GuildForumTag) { + return { + id: tag.id, + name: tag.name, + moderated: tag.moderated, + emoji_id: tag.emojiID, + emoji_name: tag.emojiName + } + } + return tag + }) + } + + const body: ModifyGuildForumChannelPayload = { + name: options?.name, + position: options?.position, + permission_overwrites: options?.permissionOverwrites, + parent_id: options?.parentID, + nsfw: options?.nsfw, + topic: options?.topic, + rate_limit_per_user: options?.slowmode, + default_auto_archive_duration: options?.defaultAutoArchiveDuration, + default_thread_rate_limit_per_user: options?.defaultThreadSlowmode, + default_sort_order: options?.defaultSortOrder, + default_reaction_emoji: options?.defaultReactionEmoji, + available_tags: options?.availableTags + } + + const resp = await this.client.rest.patch(CHANNEL(this.id), body) + + return new GuildForumChannel(this.client, resp, this.guild) + } +} diff --git a/src/types/channel.ts b/src/types/channel.ts index 7a792a20..89b989d0 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -11,6 +11,8 @@ import type { MessageComponentData, MessageComponentPayload } from './messageComponents.ts' +import type { Emoji } from '../structures/emoji.ts' +import type { GuildForumTag } from '../structures/guildForumChannel.ts' export interface ChannelPayload { id: string @@ -58,6 +60,8 @@ export interface ThreadChannelPayload export interface GuildTextChannelPayload extends GuildTextBasedChannelPayload { rate_limit_per_user: number + default_thread_rate_limit_per_user: number + default_auto_archive_duration: number } export interface GuildNewsChannelPayload extends GuildTextBasedChannelPayload {} @@ -89,9 +93,7 @@ export enum GuildForumSortOrderTypes { CREATION_DATE = 1 } -export interface GuildForumChannelPayload extends GuildChannelPayload { - default_thread_rate_limit_per_user: number - rate_limit_per_user: number +export interface GuildForumChannelPayload extends GuildTextChannelPayload { available_tags: GuildForumTagPayload[] default_reaction_emoji: GuildForumDefaultReactionPayload | null default_sort_order: GuildForumSortOrderTypes | null @@ -131,6 +133,8 @@ export interface ModifyGuildTextBasedChannelPayload export interface ModifyGuildTextChannelPayload extends ModifyGuildTextBasedChannelPayload { rate_limit_per_user?: number | null + default_thread_rate_limit_per_user?: number | null + default_auto_archive_duration?: number | null } export interface ModifyThreadChannelPayload @@ -148,6 +152,13 @@ export interface ModifyVoiceChannelPayload extends ModifyChannelPayload { user_limit?: number | null } +export interface ModifyGuildForumChannelPayload + extends ModifyGuildTextChannelPayload { + default_reaction_emoji?: GuildForumDefaultReactionPayload | null + default_sort_order?: GuildForumSortOrderTypes | null + available_tags?: GuildForumTagPayload[] | null +} + export interface ModifyChannelOption { name?: string position?: number | null @@ -166,6 +177,8 @@ export interface ModifyGuildTextBasedChannelOption extends ModifyChannelOption { export interface ModifyGuildTextChannelOption extends ModifyGuildTextBasedChannelOption { slowmode?: number | null + defaultThreadSlowmode?: number | null + defaultAutoArchiveDuration?: number | null } export interface ModifyThreadChannelOption @@ -183,6 +196,13 @@ export interface ModifyVoiceChannelOption extends ModifyChannelOption { userLimit?: number | null } +export interface ModifyGuildForumChannelOption + extends ModifyGuildTextChannelOption { + defaultReactionEmoji?: Emoji | GuildForumDefaultReactionPayload | null + defaultSortOrder?: GuildForumSortOrderTypes | null + availableTags?: GuildForumTag[] | GuildForumTagPayload[] | null +} + export enum OverwriteType { ROLE = 0, USER = 1 From 4cf2b3ebf3eea7a583a8bec157e8580dc4fdd3b0 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 09:47:06 +0900 Subject: [PATCH 05/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20feat:=20add=20mis?= =?UTF-8?q?sing=20properties=20in=20guildTextChannel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚰️ chore: remove checkGuildTextBasedChannel, use isGuildBasedTextChannel instead --- src/structures/guildTextChannel.ts | 35 ++++++++++++++++++++---------- src/utils/channelTypes.ts | 3 ++- test/index.ts | 10 ++++----- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/structures/guildTextChannel.ts b/src/structures/guildTextChannel.ts index 1687bb0b..fdc126d4 100644 --- a/src/structures/guildTextChannel.ts +++ b/src/structures/guildTextChannel.ts @@ -10,7 +10,6 @@ import type { ModifyGuildTextChannelOption, ModifyGuildTextChannelPayload } from '../types/channel.ts' -import { ChannelTypes } from '../types/channel.ts' import type { Guild } from './guild.ts' import { CHANNEL } from '../types/endpoint.ts' import type { Message } from './message.ts' @@ -20,11 +19,6 @@ import type { CategoryChannel } from './guildCategoryChannel.ts' import type { ThreadChannel, ThreadMember } from './threadChannel.ts' import { ChannelThreadsManager } from '../managers/channelThreads.ts' -const GUILD_TEXT_BASED_CHANNEL_TYPES: ChannelTypes[] = [ - ChannelTypes.GUILD_TEXT, - ChannelTypes.GUILD_NEWS -] - export interface CreateThreadOptions { /** 2-100 character channel name */ name: string @@ -133,13 +127,10 @@ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { } } -export const checkGuildTextBasedChannel = ( - channel: TextChannel -): channel is GuildTextBasedChannel => - GUILD_TEXT_BASED_CHANNEL_TYPES.includes(channel.type) - export class GuildTextChannel extends GuildTextBasedChannel { slowmode!: number + defaultThreadSlowmode!: number + defaultAutoArchiveDuration!: number threads: ChannelThreadsManager constructor(client: Client, data: GuildTextChannelPayload, guild: Guild) { @@ -155,6 +146,10 @@ export class GuildTextChannel extends GuildTextBasedChannel { readFromData(data: GuildTextChannelPayload): void { super.readFromData(data) this.slowmode = data.rate_limit_per_user ?? this.slowmode + this.defaultThreadSlowmode = + data.default_thread_rate_limit_per_user ?? this.defaultThreadSlowmode + this.defaultAutoArchiveDuration = + data.default_auto_archive_duration ?? this.defaultAutoArchiveDuration } /** Edit the Guild Text Channel */ @@ -168,7 +163,9 @@ export class GuildTextChannel extends GuildTextBasedChannel { parent_id: options?.parentID, nsfw: options?.nsfw, topic: options?.topic, - rate_limit_per_user: options?.slowmode + rate_limit_per_user: options?.slowmode, + default_auto_archive_duration: options?.defaultAutoArchiveDuration, + default_thread_rate_limit_per_user: options?.defaultThreadSlowmode } const resp = await this.client.rest.patch(CHANNEL(this.id), body) @@ -181,6 +178,20 @@ export class GuildTextChannel extends GuildTextBasedChannel { return await this.edit({ slowmode: slowmode ?? null }) } + /** Edit Default Slowmode of the threads in the channel */ + async setDefaultThreadSlowmode( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultThreadSlowmode: slowmode ?? null }) + } + + /** Edit Default Auto Archive Duration of threads */ + async setDefaultAutoArchiveDuration( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultAutoArchiveDuration: slowmode ?? null }) + } + async startThread( options: CreateThreadOptions, message: Message | string diff --git a/src/utils/channelTypes.ts b/src/utils/channelTypes.ts index 0b0ce30e..1b972e2f 100644 --- a/src/utils/channelTypes.ts +++ b/src/utils/channelTypes.ts @@ -74,7 +74,8 @@ export function isGuildChannel(channel: Channel): channel is GuildChannel { channel.type === ChannelTypes.GUILD_STAGE_VOICE || channel.type === ChannelTypes.NEWS_THREAD || channel.type === ChannelTypes.PRIVATE_THREAD || - channel.type === ChannelTypes.PUBLIC_THREAD + channel.type === ChannelTypes.PUBLIC_THREAD || + channel.type === ChannelTypes.GUILD_FORUM ) } diff --git a/test/index.ts b/test/index.ts index d2ab921e..e4154f0d 100644 --- a/test/index.ts +++ b/test/index.ts @@ -8,12 +8,12 @@ import { EveryChannelTypes, ChannelTypes, GuildTextChannel, - checkGuildTextBasedChannel, Permissions, Collector, MessageAttachment, OverrideType, - ColorUtil + ColorUtil, + isGuildBasedTextChannel } from '../mod.ts' // import { TOKEN } from './config.ts' @@ -219,7 +219,7 @@ client.on('messageCreate', async (msg: Message) => { vs.channel?.join() } else if (msg.content === '!getOverwrites') { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!checkGuildTextBasedChannel(msg.channel)) { + if (!isGuildBasedTextChannel(msg.channel)) { return msg.channel.send("This isn't a guild text channel!") } // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion @@ -251,7 +251,7 @@ client.on('messageCreate', async (msg: Message) => { msg.channel.send(`Your permissions:\n${permissions.toArray().join('\n')}`) } else if (msg.content === '!addBasicOverwrites') { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!checkGuildTextBasedChannel(msg.channel)) { + if (!isGuildBasedTextChannel(msg.channel)) { return msg.channel.send("This isn't a guild text channel!") } if (msg.member !== undefined) { @@ -263,7 +263,7 @@ client.on('messageCreate', async (msg: Message) => { } } else if (msg.content === '!updateBasicOverwrites') { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (!checkGuildTextBasedChannel(msg.channel)) { + if (!isGuildBasedTextChannel(msg.channel)) { return msg.channel.send("This isn't a guild text channel!") } if (msg.member !== undefined) { From a8ec2ede5bd9b44e1d0751061038fe045a6347e5 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 09:48:48 +0900 Subject: [PATCH 06/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20feat:=20add=20mis?= =?UTF-8?q?sing=20properties=20in=20ThreadChannel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mod.ts | 3 +-- src/structures/threadChannel.ts | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mod.ts b/mod.ts index 1d67f800..82d29eb4 100644 --- a/mod.ts +++ b/mod.ts @@ -87,8 +87,7 @@ export { Snowflake } from './src/utils/snowflake.ts' export { TextChannel } from './src/structures/textChannel.ts' export { GuildTextBasedChannel, - GuildTextChannel, - checkGuildTextBasedChannel + GuildTextChannel } from './src/structures/guildTextChannel.ts' export type { AllMessageOptions } from './src/structures/textChannel.ts' export { MessageReaction } from './src/structures/messageReaction.ts' diff --git a/src/structures/threadChannel.ts b/src/structures/threadChannel.ts index cea3751e..735a7c1f 100644 --- a/src/structures/threadChannel.ts +++ b/src/structures/threadChannel.ts @@ -89,6 +89,9 @@ export class ThreadChannel extends GuildTextBasedChannel { members: ThreadMembersManager slowmode: number = 0 owner!: UserResolvable + totalMessageSent!: number + // currently there's no way to grab the tag by id + appliedTags: string[] = [] constructor(client: Client, data: ThreadChannelPayload, guild: Guild) { super(client, data, guild) @@ -106,6 +109,8 @@ export class ThreadChannel extends GuildTextBasedChannel { : undefined this.slowmode = data.rate_limit_per_user ?? this.slowmode this.owner = new UserResolvable(this.client, data.owner_id) ?? this.owner + this.totalMessageSent = data.total_message_sent ?? this.totalMessageSent + this.appliedTags = data.applied_tags ?? this.appliedTags } readFromData(data: ThreadChannelPayload): this { From a39f52002613406f10f9da44bc73ea06821c857c Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 09:57:57 +0900 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=9A=B8=20chore:=20move=20toString?= =?UTF-8?q?=20from=20GuildTextBasedChannel=20to=20Channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/structures/channel.ts | 4 ++++ src/structures/guildTextChannel.ts | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/structures/channel.ts b/src/structures/channel.ts index 071b3355..f5a84dea 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -54,6 +54,10 @@ export class Channel extends SnowflakeBase { return `<#${this.id}>` } + toString(): string { + return this.mention + } + constructor(client: Client, data: ChannelPayload) { super(client, data) this.readFromData(data) diff --git a/src/structures/guildTextChannel.ts b/src/structures/guildTextChannel.ts index fdc126d4..ce94dee7 100644 --- a/src/structures/guildTextChannel.ts +++ b/src/structures/guildTextChannel.ts @@ -30,14 +30,6 @@ export interface CreateThreadOptions { export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { topic?: string - get mention(): string { - return `<#${this.id}>` - } - - toString(): string { - return this.mention - } - constructor( client: Client, data: GuildTextBasedChannelPayload, From 369cafce64d0a510cdd1fb3d2ced8060fc17c0ab Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 09:58:43 +0900 Subject: [PATCH 08/15] =?UTF-8?q?=F0=9F=A9=B9=20fix:=20forum=20channel=20i?= =?UTF-8?q?s=20similar=20to=20text=20channel=20but=20it's=20not=20a=20text?= =?UTF-8?q?=20channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/structures/guildForumChannel.ts | 198 +++++++++++++++++++++++++++- 1 file changed, 195 insertions(+), 3 deletions(-) diff --git a/src/structures/guildForumChannel.ts b/src/structures/guildForumChannel.ts index be89b662..c0211e8b 100644 --- a/src/structures/guildForumChannel.ts +++ b/src/structures/guildForumChannel.ts @@ -1,13 +1,23 @@ import { + CategoryChannel, + Client, Emoji, + Guild, + GuildChannel, GuildForumChannelPayload, GuildForumSortOrderTypes, GuildForumTagPayload, - GuildTextChannel, + Invite, + Message, ModifyGuildForumChannelOption, - ModifyGuildForumChannelPayload + ModifyGuildForumChannelPayload, + ThreadChannel, + ThreadMember } from '../../mod.ts' +import { ChannelThreadsManager } from '../managers/channelThreads.ts' +import { CreateInviteOptions } from '../managers/invites.ts' import { CHANNEL } from '../types/endpoint.ts' +import { CreateThreadOptions } from './guildTextChannel.ts' export class GuildForumTag { id!: string @@ -29,13 +39,33 @@ export class GuildForumTag { } } -export class GuildForumChannel extends GuildTextChannel { +export class GuildForumChannel extends GuildChannel { + slowmode!: number + defaultThreadSlowmode!: number + defaultAutoArchiveDuration!: number + threads: ChannelThreadsManager + topic?: string availableTags!: GuildForumTag[] defaultReactionEmoji!: Emoji defaultSortOrder!: GuildForumSortOrderTypes + constructor(client: Client, data: GuildForumChannelPayload, guild: Guild) { + super(client, data, guild) + this.readFromData(data) + this.threads = new ChannelThreadsManager( + this.client, + this.guild.threads, + this + ) + } + readFromData(data: GuildForumChannelPayload): void { super.readFromData(data) + this.topic = data.topic ?? this.topic + this.defaultThreadSlowmode = + data.default_thread_rate_limit_per_user ?? this.defaultThreadSlowmode + this.defaultAutoArchiveDuration = + data.default_auto_archive_duration ?? this.defaultAutoArchiveDuration this.slowmode = data.rate_limit_per_user ?? this.slowmode this.availableTags = data.available_tags?.map((tag) => new GuildForumTag(tag)) ?? @@ -94,4 +124,166 @@ export class GuildForumChannel extends GuildTextChannel { return new GuildForumChannel(this.client, resp, this.guild) } + + /** Create an Invite for this Channel */ + async createInvite(options?: CreateInviteOptions): Promise { + return this.guild.invites.create(this.id, options) + } + + /** Edit topic of the channel */ + async setTopic(topic: string): Promise { + return await this.edit({ topic }) + } + + /** Edit category of the channel */ + async setCategory( + category: CategoryChannel | string + ): Promise { + return await this.edit({ + parentID: typeof category === 'object' ? category.id : category + }) + } + + /** Edit Slowmode of the channel */ + async setSlowmode(slowmode?: number | null): Promise { + return await this.edit({ slowmode: slowmode ?? null }) + } + + /** Edit Default Slowmode of the threads in the channel */ + async setDefaultThreadSlowmode( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultThreadSlowmode: slowmode ?? null }) + } + + /** Edit Default Auto Archive Duration of threads */ + async setDefaultAutoArchiveDuration( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultAutoArchiveDuration: slowmode ?? null }) + } + + async startThread( + options: CreateThreadOptions, + message: Message | string + ): Promise { + const payload = await this.client.rest.endpoints.startPublicThread( + this.id, + typeof message === 'string' ? message : message.id, + { name: options.name, auto_archive_duration: options.autoArchiveDuration } + ) + await this.client.channels.set(payload.id, payload) + return (await this.client.channels.get(payload.id))! + } + + async startPrivateThread( + options: CreateThreadOptions + ): Promise { + const payload = await this.client.rest.endpoints.startPrivateThread( + this.id, + { name: options.name, auto_archive_duration: options.autoArchiveDuration } + ) + await this.client.channels.set(payload.id, payload) + return (await this.client.channels.get(payload.id))! + } + + async fetchArchivedThreads( + type: 'public' | 'private' = 'public', + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + const data = + type === 'public' + ? await this.client.rest.endpoints.getPublicArchivedThreads( + this.id, + params + ) + : await this.client.rest.endpoints.getPrivateArchivedThreads( + this.id, + params + ) + + const threads: ThreadChannel[] = [] + const members: ThreadMember[] = [] + + for (const d of data.threads) { + await this.threads.set(d.id, d) + threads.push((await this.threads.get(d.id))!) + } + + for (const d of data.members) { + const thread = + threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) + if (thread !== undefined) { + await thread.members.set(d.user_id, d) + members.push((await thread.members.get(d.user_id))!) + } + } + + return { + threads, + members, + hasMore: data.has_more + } + } + + async fetchPublicArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + return await this.fetchArchivedThreads('public', params) + } + + async fetchPrivateArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + return await this.fetchArchivedThreads('private', params) + } + + async fetchJoinedPrivateArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + const data = + await this.client.rest.endpoints.getJoinedPrivateArchivedThreads( + this.id, + params + ) + + const threads: ThreadChannel[] = [] + const members: ThreadMember[] = [] + + for (const d of data.threads) { + await this.threads.set(d.id, d) + threads.push((await this.threads.get(d.id))!) + } + + for (const d of data.members) { + const thread = + threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) + if (thread !== undefined) { + await thread.members.set(d.user_id, d) + members.push((await thread.members.get(d.user_id))!) + } + } + + return { + threads, + members, + hasMore: data.has_more + } + } } From f4cbdf0b8471aadaac05437261522eaab82e37e4 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 4 Oct 2022 10:59:59 +0900 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20fix:=20channel=20?= =?UTF-8?q?types=20are=20wrong=20typed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mod.ts | 4 + src/managers/channelThreads.ts | 10 +- src/structures/channel.ts | 7 + src/structures/guildForumChannel.ts | 200 +-------------- src/structures/guildNewsChannel.ts | 7 +- src/structures/guildTextChannel.ts | 231 +----------------- src/structures/guildThreadAvailableChannel.ts | 215 ++++++++++++++++ src/types/channel.ts | 53 ++-- src/utils/channelTypes.ts | 11 + 9 files changed, 297 insertions(+), 441 deletions(-) create mode 100644 src/structures/guildThreadAvailableChannel.ts diff --git a/mod.ts b/mod.ts index 82d29eb4..498b1a6e 100644 --- a/mod.ts +++ b/mod.ts @@ -67,6 +67,10 @@ export { GuildIntegration } from './src/structures/guild.ts' export { CategoryChannel } from './src/structures/guildCategoryChannel.ts' +export { + GuildForumChannel, + GuildForumTag +} from './src/structures/guildForumChannel.ts' export { NewsChannel } from './src/structures/guildNewsChannel.ts' export { VoiceChannel } from './src/structures/guildVoiceChannel.ts' export { Invite } from './src/structures/invite.ts' diff --git a/src/managers/channelThreads.ts b/src/managers/channelThreads.ts index 628bb803..270be0e8 100644 --- a/src/managers/channelThreads.ts +++ b/src/managers/channelThreads.ts @@ -6,23 +6,21 @@ import type { ThreadChannel, ThreadMember } from '../structures/threadChannel.ts' -import type { - CreateThreadOptions, - GuildTextChannel -} from '../structures/guildTextChannel.ts' +import type { CreateThreadOptions } from '../structures/guildTextChannel.ts' import type { BaseManager, Message } from '../../mod.ts' +import type { GuildThreadAvailableChannel } from '../structures/guildThreadAvailableChannel.ts' export class ChannelThreadsManager extends BaseChildManager< ThreadChannelPayload, ThreadChannel > { - channel: GuildTextChannel + channel: GuildThreadAvailableChannel declare parent: BaseManager constructor( client: Client, parent: ThreadsManager, - channel: GuildTextChannel + channel: GuildThreadAvailableChannel ) { super( client, diff --git a/src/structures/channel.ts b/src/structures/channel.ts index f5a84dea..0525b19c 100644 --- a/src/structures/channel.ts +++ b/src/structures/channel.ts @@ -43,6 +43,8 @@ import type { VoiceChannel } from '../structures/guildVoiceChannel.ts' import type { StageVoiceChannel } from '../structures/guildVoiceStageChannel.ts' import type { TextChannel } from '../structures/textChannel.ts' import type { ThreadChannel } from '../structures/threadChannel.ts' +import { CreateInviteOptions } from '../managers/invites.ts' +import { Invite } from './invite.ts' export class Channel extends SnowflakeBase { type!: ChannelTypes @@ -441,4 +443,9 @@ export class GuildChannel extends Channel { async setPosition(position: number): Promise { return await this.edit({ position }) } + + /** Create an Invite for this Channel */ + async createInvite(options?: CreateInviteOptions): Promise { + return this.guild.invites.create(this.id, options) + } } diff --git a/src/structures/guildForumChannel.ts b/src/structures/guildForumChannel.ts index c0211e8b..4b581126 100644 --- a/src/structures/guildForumChannel.ts +++ b/src/structures/guildForumChannel.ts @@ -1,23 +1,15 @@ +import { Client } from '../client/client.ts' import { - CategoryChannel, - Client, - Emoji, - Guild, - GuildChannel, GuildForumChannelPayload, GuildForumSortOrderTypes, GuildForumTagPayload, - Invite, - Message, ModifyGuildForumChannelOption, - ModifyGuildForumChannelPayload, - ThreadChannel, - ThreadMember -} from '../../mod.ts' -import { ChannelThreadsManager } from '../managers/channelThreads.ts' -import { CreateInviteOptions } from '../managers/invites.ts' + ModifyGuildForumChannelPayload +} from '../types/channel.ts' import { CHANNEL } from '../types/endpoint.ts' -import { CreateThreadOptions } from './guildTextChannel.ts' +import { Emoji } from './emoji.ts' +import { Guild } from './guild.ts' +import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts' export class GuildForumTag { id!: string @@ -39,12 +31,7 @@ export class GuildForumTag { } } -export class GuildForumChannel extends GuildChannel { - slowmode!: number - defaultThreadSlowmode!: number - defaultAutoArchiveDuration!: number - threads: ChannelThreadsManager - topic?: string +export class GuildForumChannel extends GuildThreadAvailableChannel { availableTags!: GuildForumTag[] defaultReactionEmoji!: Emoji defaultSortOrder!: GuildForumSortOrderTypes @@ -52,21 +39,10 @@ export class GuildForumChannel extends GuildChannel { constructor(client: Client, data: GuildForumChannelPayload, guild: Guild) { super(client, data, guild) this.readFromData(data) - this.threads = new ChannelThreadsManager( - this.client, - this.guild.threads, - this - ) } readFromData(data: GuildForumChannelPayload): void { super.readFromData(data) - this.topic = data.topic ?? this.topic - this.defaultThreadSlowmode = - data.default_thread_rate_limit_per_user ?? this.defaultThreadSlowmode - this.defaultAutoArchiveDuration = - data.default_auto_archive_duration ?? this.defaultAutoArchiveDuration - this.slowmode = data.rate_limit_per_user ?? this.slowmode this.availableTags = data.available_tags?.map((tag) => new GuildForumTag(tag)) ?? this.availableTags @@ -124,166 +100,4 @@ export class GuildForumChannel extends GuildChannel { return new GuildForumChannel(this.client, resp, this.guild) } - - /** Create an Invite for this Channel */ - async createInvite(options?: CreateInviteOptions): Promise { - return this.guild.invites.create(this.id, options) - } - - /** Edit topic of the channel */ - async setTopic(topic: string): Promise { - return await this.edit({ topic }) - } - - /** Edit category of the channel */ - async setCategory( - category: CategoryChannel | string - ): Promise { - return await this.edit({ - parentID: typeof category === 'object' ? category.id : category - }) - } - - /** Edit Slowmode of the channel */ - async setSlowmode(slowmode?: number | null): Promise { - return await this.edit({ slowmode: slowmode ?? null }) - } - - /** Edit Default Slowmode of the threads in the channel */ - async setDefaultThreadSlowmode( - slowmode?: number | null - ): Promise { - return await this.edit({ defaultThreadSlowmode: slowmode ?? null }) - } - - /** Edit Default Auto Archive Duration of threads */ - async setDefaultAutoArchiveDuration( - slowmode?: number | null - ): Promise { - return await this.edit({ defaultAutoArchiveDuration: slowmode ?? null }) - } - - async startThread( - options: CreateThreadOptions, - message: Message | string - ): Promise { - const payload = await this.client.rest.endpoints.startPublicThread( - this.id, - typeof message === 'string' ? message : message.id, - { name: options.name, auto_archive_duration: options.autoArchiveDuration } - ) - await this.client.channels.set(payload.id, payload) - return (await this.client.channels.get(payload.id))! - } - - async startPrivateThread( - options: CreateThreadOptions - ): Promise { - const payload = await this.client.rest.endpoints.startPrivateThread( - this.id, - { name: options.name, auto_archive_duration: options.autoArchiveDuration } - ) - await this.client.channels.set(payload.id, payload) - return (await this.client.channels.get(payload.id))! - } - - async fetchArchivedThreads( - type: 'public' | 'private' = 'public', - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - const data = - type === 'public' - ? await this.client.rest.endpoints.getPublicArchivedThreads( - this.id, - params - ) - : await this.client.rest.endpoints.getPrivateArchivedThreads( - this.id, - params - ) - - const threads: ThreadChannel[] = [] - const members: ThreadMember[] = [] - - for (const d of data.threads) { - await this.threads.set(d.id, d) - threads.push((await this.threads.get(d.id))!) - } - - for (const d of data.members) { - const thread = - threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) - if (thread !== undefined) { - await thread.members.set(d.user_id, d) - members.push((await thread.members.get(d.user_id))!) - } - } - - return { - threads, - members, - hasMore: data.has_more - } - } - - async fetchPublicArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - return await this.fetchArchivedThreads('public', params) - } - - async fetchPrivateArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - return await this.fetchArchivedThreads('private', params) - } - - async fetchJoinedPrivateArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - const data = - await this.client.rest.endpoints.getJoinedPrivateArchivedThreads( - this.id, - params - ) - - const threads: ThreadChannel[] = [] - const members: ThreadMember[] = [] - - for (const d of data.threads) { - await this.threads.set(d.id, d) - threads.push((await this.threads.get(d.id))!) - } - - for (const d of data.members) { - const thread = - threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) - if (thread !== undefined) { - await thread.members.set(d.user_id, d) - members.push((await thread.members.get(d.user_id))!) - } - } - - return { - threads, - members, - hasMore: data.has_more - } - } } diff --git a/src/structures/guildNewsChannel.ts b/src/structures/guildNewsChannel.ts index e6f8f5e1..1c96fb25 100644 --- a/src/structures/guildNewsChannel.ts +++ b/src/structures/guildNewsChannel.ts @@ -1,3 +1,8 @@ +import { Mixin } from '../../deps.ts' import { GuildTextBasedChannel } from './guildTextChannel.ts' +import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts' -export class NewsChannel extends GuildTextBasedChannel {} +export class NewsChannel extends Mixin( + GuildTextBasedChannel, + GuildThreadAvailableChannel +) {} diff --git a/src/structures/guildTextChannel.ts b/src/structures/guildTextChannel.ts index ce94dee7..88f7547a 100644 --- a/src/structures/guildTextChannel.ts +++ b/src/structures/guildTextChannel.ts @@ -4,20 +4,13 @@ import { GuildChannel } from './channel.ts' import type { Client } from '../client/mod.ts' import type { GuildTextBasedChannelPayload, - GuildTextChannelPayload, ModifyGuildTextBasedChannelOption, - ModifyGuildTextBasedChannelPayload, - ModifyGuildTextChannelOption, - ModifyGuildTextChannelPayload + ModifyGuildTextBasedChannelPayload } from '../types/channel.ts' import type { Guild } from './guild.ts' import { CHANNEL } from '../types/endpoint.ts' import type { Message } from './message.ts' -import type { CreateInviteOptions } from '../managers/invites.ts' -import type { Invite } from './invite.ts' -import type { CategoryChannel } from './guildCategoryChannel.ts' -import type { ThreadChannel, ThreadMember } from './threadChannel.ts' -import { ChannelThreadsManager } from '../managers/channelThreads.ts' +import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts' export interface CreateThreadOptions { /** 2-100 character channel name */ @@ -28,8 +21,6 @@ export interface CreateThreadOptions { /** Represents a Text Channel but in a Guild */ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { - topic?: string - constructor( client: Client, data: GuildTextBasedChannelPayload, @@ -41,7 +32,6 @@ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { readFromData(data: GuildTextBasedChannelPayload): void { super.readFromData(data) - this.topic = data.topic ?? this.topic } /** Edit the Guild Text Channel */ @@ -53,9 +43,7 @@ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { position: options?.position, permission_overwrites: options?.permissionOverwrites, parent_id: options?.parentID, - nsfw: options?.nsfw, - topic: options?.topic - // rate_limit_per_user: options?.slowmode + nsfw: options?.nsfw } const resp = await this.client.rest.patch(CHANNEL(this.id), body) @@ -98,213 +86,10 @@ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { return this } - - /** Create an Invite for this Channel */ - async createInvite(options?: CreateInviteOptions): Promise { - return this.guild.invites.create(this.id, options) - } - - /** Edit topic of the channel */ - async setTopic(topic: string): Promise { - return await this.edit({ topic }) - } - - /** Edit category of the channel */ - async setCategory( - category: CategoryChannel | string - ): Promise { - return await this.edit({ - parentID: typeof category === 'object' ? category.id : category - }) - } } -export class GuildTextChannel extends GuildTextBasedChannel { - slowmode!: number - defaultThreadSlowmode!: number - defaultAutoArchiveDuration!: number - threads: ChannelThreadsManager - - constructor(client: Client, data: GuildTextChannelPayload, guild: Guild) { - super(client, data, guild) - this.readFromData(data) - this.threads = new ChannelThreadsManager( - this.client, - this.guild.threads, - this - ) - } - - readFromData(data: GuildTextChannelPayload): void { - super.readFromData(data) - this.slowmode = data.rate_limit_per_user ?? this.slowmode - this.defaultThreadSlowmode = - data.default_thread_rate_limit_per_user ?? this.defaultThreadSlowmode - this.defaultAutoArchiveDuration = - data.default_auto_archive_duration ?? this.defaultAutoArchiveDuration - } - - /** Edit the Guild Text Channel */ - async edit( - options?: ModifyGuildTextChannelOption - ): Promise { - const body: ModifyGuildTextChannelPayload = { - name: options?.name, - position: options?.position, - permission_overwrites: options?.permissionOverwrites, - parent_id: options?.parentID, - nsfw: options?.nsfw, - topic: options?.topic, - rate_limit_per_user: options?.slowmode, - default_auto_archive_duration: options?.defaultAutoArchiveDuration, - default_thread_rate_limit_per_user: options?.defaultThreadSlowmode - } - - const resp = await this.client.rest.patch(CHANNEL(this.id), body) - - return new GuildTextChannel(this.client, resp, this.guild) - } - - /** Edit Slowmode of the channel */ - async setSlowmode(slowmode?: number | null): Promise { - return await this.edit({ slowmode: slowmode ?? null }) - } - - /** Edit Default Slowmode of the threads in the channel */ - async setDefaultThreadSlowmode( - slowmode?: number | null - ): Promise { - return await this.edit({ defaultThreadSlowmode: slowmode ?? null }) - } - - /** Edit Default Auto Archive Duration of threads */ - async setDefaultAutoArchiveDuration( - slowmode?: number | null - ): Promise { - return await this.edit({ defaultAutoArchiveDuration: slowmode ?? null }) - } - - async startThread( - options: CreateThreadOptions, - message: Message | string - ): Promise { - const payload = await this.client.rest.endpoints.startPublicThread( - this.id, - typeof message === 'string' ? message : message.id, - { name: options.name, auto_archive_duration: options.autoArchiveDuration } - ) - await this.client.channels.set(payload.id, payload) - return (await this.client.channels.get(payload.id))! - } - - async startPrivateThread( - options: CreateThreadOptions - ): Promise { - const payload = await this.client.rest.endpoints.startPrivateThread( - this.id, - { name: options.name, auto_archive_duration: options.autoArchiveDuration } - ) - await this.client.channels.set(payload.id, payload) - return (await this.client.channels.get(payload.id))! - } - - async fetchArchivedThreads( - type: 'public' | 'private' = 'public', - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - const data = - type === 'public' - ? await this.client.rest.endpoints.getPublicArchivedThreads( - this.id, - params - ) - : await this.client.rest.endpoints.getPrivateArchivedThreads( - this.id, - params - ) - - const threads: ThreadChannel[] = [] - const members: ThreadMember[] = [] - - for (const d of data.threads) { - await this.threads.set(d.id, d) - threads.push((await this.threads.get(d.id))!) - } - - for (const d of data.members) { - const thread = - threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) - if (thread !== undefined) { - await thread.members.set(d.user_id, d) - members.push((await thread.members.get(d.user_id))!) - } - } - - return { - threads, - members, - hasMore: data.has_more - } - } - - async fetchPublicArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - return await this.fetchArchivedThreads('public', params) - } - - async fetchPrivateArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - return await this.fetchArchivedThreads('private', params) - } - - async fetchJoinedPrivateArchivedThreads( - params: { before?: string; limit?: number } = {} - ): Promise<{ - threads: ThreadChannel[] - members: ThreadMember[] - hasMore: boolean - }> { - const data = - await this.client.rest.endpoints.getJoinedPrivateArchivedThreads( - this.id, - params - ) - - const threads: ThreadChannel[] = [] - const members: ThreadMember[] = [] - - for (const d of data.threads) { - await this.threads.set(d.id, d) - threads.push((await this.threads.get(d.id))!) - } - - for (const d of data.members) { - const thread = - threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) - if (thread !== undefined) { - await thread.members.set(d.user_id, d) - members.push((await thread.members.get(d.user_id))!) - } - } - - return { - threads, - members, - hasMore: data.has_more - } - } -} +// Still exist for API compatibility +export class GuildTextChannel extends Mixin( + GuildTextBasedChannel, + GuildThreadAvailableChannel +) {} diff --git a/src/structures/guildThreadAvailableChannel.ts b/src/structures/guildThreadAvailableChannel.ts new file mode 100644 index 00000000..b5197025 --- /dev/null +++ b/src/structures/guildThreadAvailableChannel.ts @@ -0,0 +1,215 @@ +import { Client } from '../client/client.ts' +import { ChannelThreadsManager } from '../managers/channelThreads.ts' +import { + GuildThreadAvailableChannelPayload, + ModifyGuildThreadAvailableChannelOption, + ModifyGuildThreadAvailableChannelPayload +} from '../types/channel.ts' +import { CHANNEL } from '../types/endpoint.ts' +import { GuildChannel } from './channel.ts' +import { Guild } from './guild.ts' +import { CreateThreadOptions } from './guildTextChannel.ts' +import { Message } from './message.ts' +import { ThreadChannel, ThreadMember } from './threadChannel.ts' + +export class GuildThreadAvailableChannel extends GuildChannel { + topic?: string + slowmode!: number + defaultThreadSlowmode?: number + defaultAutoArchiveDuration?: number + threads: ChannelThreadsManager + + constructor( + client: Client, + data: GuildThreadAvailableChannelPayload, + guild: Guild + ) { + super(client, data, guild) + this.readFromData(data) + this.threads = new ChannelThreadsManager( + this.client, + this.guild.threads, + this + ) + } + + readFromData(data: GuildThreadAvailableChannelPayload): void { + super.readFromData(data) + this.defaultThreadSlowmode = + data.default_thread_rate_limit_per_user ?? this.defaultThreadSlowmode + this.defaultAutoArchiveDuration = + data.default_auto_archive_duration ?? this.defaultAutoArchiveDuration + this.topic = data.topic ?? this.topic + } + + /** Edit the Guild Text Channel */ + async edit( + options?: ModifyGuildThreadAvailableChannelOption + ): Promise { + const body: ModifyGuildThreadAvailableChannelPayload = { + name: options?.name, + position: options?.position, + permission_overwrites: options?.permissionOverwrites, + parent_id: options?.parentID, + nsfw: options?.nsfw, + topic: options?.topic, + rate_limit_per_user: options?.slowmode, + default_auto_archive_duration: options?.defaultAutoArchiveDuration, + default_thread_rate_limit_per_user: options?.defaultThreadSlowmode + } + + const resp = await this.client.rest.patch(CHANNEL(this.id), body) + + return new GuildThreadAvailableChannel(this.client, resp, this.guild) + } + + /** Edit topic of the channel */ + async setTopic(topic: string): Promise { + return await this.edit({ topic }) + } + + /** Edit Slowmode of the channel */ + async setSlowmode( + slowmode?: number | null + ): Promise { + return await this.edit({ slowmode: slowmode ?? null }) + } + + /** Edit Default Slowmode of the threads in the channel */ + async setDefaultThreadSlowmode( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultThreadSlowmode: slowmode ?? null }) + } + + /** Edit Default Auto Archive Duration of threads */ + async setDefaultAutoArchiveDuration( + slowmode?: number | null + ): Promise { + return await this.edit({ defaultAutoArchiveDuration: slowmode ?? null }) + } + + async startThread( + options: CreateThreadOptions, + message: Message | string + ): Promise { + const payload = await this.client.rest.endpoints.startPublicThread( + this.id, + typeof message === 'string' ? message : message.id, + { name: options.name, auto_archive_duration: options.autoArchiveDuration } + ) + await this.client.channels.set(payload.id, payload) + return (await this.client.channels.get(payload.id))! + } + + async startPrivateThread( + options: CreateThreadOptions + ): Promise { + const payload = await this.client.rest.endpoints.startPrivateThread( + this.id, + { name: options.name, auto_archive_duration: options.autoArchiveDuration } + ) + await this.client.channels.set(payload.id, payload) + return (await this.client.channels.get(payload.id))! + } + + async fetchArchivedThreads( + type: 'public' | 'private' = 'public', + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + const data = + type === 'public' + ? await this.client.rest.endpoints.getPublicArchivedThreads( + this.id, + params + ) + : await this.client.rest.endpoints.getPrivateArchivedThreads( + this.id, + params + ) + + const threads: ThreadChannel[] = [] + const members: ThreadMember[] = [] + + for (const d of data.threads) { + await this.threads.set(d.id, d) + threads.push((await this.threads.get(d.id))!) + } + + for (const d of data.members) { + const thread = + threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) + if (thread !== undefined) { + await thread.members.set(d.user_id, d) + members.push((await thread.members.get(d.user_id))!) + } + } + + return { + threads, + members, + hasMore: data.has_more + } + } + + async fetchPublicArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + return await this.fetchArchivedThreads('public', params) + } + + async fetchPrivateArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + return await this.fetchArchivedThreads('private', params) + } + + async fetchJoinedPrivateArchivedThreads( + params: { before?: string; limit?: number } = {} + ): Promise<{ + threads: ThreadChannel[] + members: ThreadMember[] + hasMore: boolean + }> { + const data = + await this.client.rest.endpoints.getJoinedPrivateArchivedThreads( + this.id, + params + ) + + const threads: ThreadChannel[] = [] + const members: ThreadMember[] = [] + + for (const d of data.threads) { + await this.threads.set(d.id, d) + threads.push((await this.threads.get(d.id))!) + } + + for (const d of data.members) { + const thread = + threads.find((e) => e.id === d.id) ?? (await this.threads.get(d.id)) + if (thread !== undefined) { + await thread.members.set(d.user_id, d) + members.push((await thread.members.get(d.user_id))!) + } + } + + return { + threads, + members, + hasMore: data.has_more + } + } +} diff --git a/src/types/channel.ts b/src/types/channel.ts index 89b989d0..ee90c8b5 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -36,8 +36,14 @@ export interface GuildChannelPayload extends ChannelPayload { export interface GuildTextBasedChannelPayload extends TextChannelPayload, - GuildChannelPayload { + GuildChannelPayload {} + +export interface GuildThreadAvailableChannelPayload + extends GuildChannelPayload { topic?: string + rate_limit_per_user: number + default_thread_rate_limit_per_user?: number + default_auto_archive_duration?: number } export enum ChannelFlags { @@ -58,13 +64,13 @@ export interface ThreadChannelPayload applied_tags?: string[] } -export interface GuildTextChannelPayload extends GuildTextBasedChannelPayload { - rate_limit_per_user: number - default_thread_rate_limit_per_user: number - default_auto_archive_duration: number -} +export interface GuildTextChannelPayload + extends GuildTextBasedChannelPayload, + GuildThreadAvailableChannelPayload {} -export interface GuildNewsChannelPayload extends GuildTextBasedChannelPayload {} +export interface GuildNewsChannelPayload + extends GuildTextBasedChannelPayload, + GuildThreadAvailableChannelPayload {} export interface GuildVoiceChannelPayload extends GuildChannelPayload { bitrate: string @@ -93,7 +99,8 @@ export enum GuildForumSortOrderTypes { CREATION_DATE = 1 } -export interface GuildForumChannelPayload extends GuildTextChannelPayload { +export interface GuildForumChannelPayload + extends GuildThreadAvailableChannelPayload { available_tags: GuildForumTagPayload[] default_reaction_emoji: GuildForumDefaultReactionPayload | null default_sort_order: GuildForumSortOrderTypes | null @@ -127,16 +134,20 @@ export interface ModifyGuildCategoryChannelPayload export interface ModifyGuildTextBasedChannelPayload extends ModifyChannelPayload { type?: number - topic?: string | null } -export interface ModifyGuildTextChannelPayload - extends ModifyGuildTextBasedChannelPayload { +export interface ModifyGuildThreadAvailableChannelPayload + extends ModifyChannelPayload { + topic?: string | null rate_limit_per_user?: number | null default_thread_rate_limit_per_user?: number | null default_auto_archive_duration?: number | null } +export interface ModifyGuildTextChannelPayload + extends ModifyGuildTextBasedChannelPayload, + ModifyGuildThreadAvailableChannelPayload {} + export interface ModifyThreadChannelPayload extends ModifyGuildTextBasedChannelPayload { archived?: boolean @@ -145,7 +156,8 @@ export interface ModifyThreadChannelPayload } export interface ModifyGuildNewsChannelPayload - extends ModifyGuildTextBasedChannelPayload {} + extends ModifyGuildTextBasedChannelPayload, + ModifyGuildThreadAvailableChannelPayload {} export interface ModifyVoiceChannelPayload extends ModifyChannelPayload { bitrate?: number | null @@ -153,7 +165,7 @@ export interface ModifyVoiceChannelPayload extends ModifyChannelPayload { } export interface ModifyGuildForumChannelPayload - extends ModifyGuildTextChannelPayload { + extends ModifyGuildThreadAvailableChannelPayload { default_reaction_emoji?: GuildForumDefaultReactionPayload | null default_sort_order?: GuildForumSortOrderTypes | null available_tags?: GuildForumTagPayload[] | null @@ -171,16 +183,20 @@ export interface ModifyGuildCategoryChannelOption extends ModifyChannelOption {} export interface ModifyGuildTextBasedChannelOption extends ModifyChannelOption { type?: number - topic?: string | null } -export interface ModifyGuildTextChannelOption - extends ModifyGuildTextBasedChannelOption { +export interface ModifyGuildThreadAvailableChannelOption + extends ModifyChannelOption { + topic?: string | null slowmode?: number | null defaultThreadSlowmode?: number | null defaultAutoArchiveDuration?: number | null } +export interface ModifyGuildTextChannelOption + extends ModifyGuildTextBasedChannelOption, + ModifyGuildThreadAvailableChannelOption {} + export interface ModifyThreadChannelOption extends ModifyGuildTextChannelOption { archived?: boolean @@ -189,7 +205,8 @@ export interface ModifyThreadChannelOption } export interface ModifyGuildNewsChannelOption - extends ModifyGuildTextBasedChannelOption {} + extends ModifyGuildTextBasedChannelOption, + ModifyGuildThreadAvailableChannelOption {} export interface ModifyVoiceChannelOption extends ModifyChannelOption { bitrate?: number | null @@ -197,7 +214,7 @@ export interface ModifyVoiceChannelOption extends ModifyChannelOption { } export interface ModifyGuildForumChannelOption - extends ModifyGuildTextChannelOption { + extends ModifyGuildThreadAvailableChannelOption { defaultReactionEmoji?: Emoji | GuildForumDefaultReactionPayload | null defaultSortOrder?: GuildForumSortOrderTypes | null availableTags?: GuildForumTag[] | GuildForumTagPayload[] | null diff --git a/src/utils/channelTypes.ts b/src/utils/channelTypes.ts index 1b972e2f..17d3b064 100644 --- a/src/utils/channelTypes.ts +++ b/src/utils/channelTypes.ts @@ -8,6 +8,7 @@ import type { GuildTextBasedChannel, GuildTextChannel } from '../structures/guildTextChannel.ts' +import { GuildThreadAvailableChannel } from '../structures/guildThreadAvailableChannel.ts' import type { VoiceChannel } from '../structures/guildVoiceChannel.ts' import type { StageVoiceChannel } from '../structures/guildVoiceStageChannel.ts' import type { TextChannel } from '../structures/textChannel.ts' @@ -98,3 +99,13 @@ export function isTextChannel(channel: Channel): channel is TextChannel { channel.type === ChannelTypes.PUBLIC_THREAD ) } + +export function isThreadAvailableChannel( + channel: Channel +): channel is GuildThreadAvailableChannel { + return ( + channel.type === ChannelTypes.GUILD_TEXT || + channel.type === ChannelTypes.GUILD_NEWS || + channel.type === ChannelTypes.GUILD_FORUM + ) +} From 0fe6948440bdb8740f49c727319705bc37a5d143 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Sun, 9 Oct 2022 21:55:15 +0900 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20fix:=20guild=20fo?= =?UTF-8?q?rum=20channel=20not=20in=20guild=20channels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/types/guild.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/types/guild.ts b/src/types/guild.ts index 6d5314cb..c3943993 100644 --- a/src/types/guild.ts +++ b/src/types/guild.ts @@ -8,6 +8,7 @@ import type { GuildTextChannel, GuildTextBasedChannel } from '../structures/guildTextChannel.ts' +import type { GuildForumChannel } from '../structures/guildForumChannel.ts' import type { ApplicationPayload } from './application.ts' import type { ChannelPayload, @@ -18,7 +19,8 @@ import type { GuildTextChannelPayload, GuildVoiceChannelPayload, ThreadChannelPayload, - MessageStickerPayload + MessageStickerPayload, + GuildForumChannelPayload } from './channel.ts' import type { EmojiPayload } from './emoji.ts' import type { PresenceUpdatePayload } from './gateway.ts' @@ -190,6 +192,11 @@ export interface GuildWidgetPayload { presence_count: number } +export type GuildThreadAvailableChannelPayloads = + | GuildTextChannelPayload + | GuildNewsChannelPayload + | GuildForumChannelPayload + export type GuildTextBasedPayloads = | GuildTextBasedChannelPayload | GuildTextChannelPayload @@ -197,11 +204,18 @@ export type GuildTextBasedPayloads = export type GuildChannelPayloads = | GuildTextBasedPayloads + | GuildThreadAvailableChannelPayloads | GuildVoiceChannelPayload | GuildCategoryChannelPayload +export type GuildThreadAvailableChannels = + | GuildTextChannel + | NewsChannel + | GuildForumChannel + export type GuildTextBasedChannels = | GuildTextBasedChannel + | GuildThreadAvailableChannels | GuildTextChannel | NewsChannel From c2b2f95abfb913aa84fd3b1af0e26b9048e52638 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Sun, 9 Oct 2022 22:09:12 +0900 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20fix:=20guild=20fo?= =?UTF-8?q?rum=20channel=20not=20available=20from=20channel=20manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/channel.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/utils/channel.ts b/src/utils/channel.ts index 7caeef8d..1830463f 100644 --- a/src/utils/channel.ts +++ b/src/utils/channel.ts @@ -5,6 +5,7 @@ import { DMChannelPayload, GroupDMChannelPayload, GuildCategoryChannelPayload, + GuildForumChannelPayload, GuildNewsChannelPayload, GuildStageChannelPayload, GuildTextBasedChannelPayload, @@ -28,6 +29,7 @@ import { Channel, GuildChannel } from '../structures/channel.ts' import { StoreChannel } from '../structures/guildStoreChannel.ts' import { StageVoiceChannel } from '../structures/guildStageVoiceChannel.ts' import { ThreadChannel } from '../structures/threadChannel.ts' +import { GuildForumChannel } from '../structures/guildForumChannel.ts' export type EveryTextChannelTypes = | TextChannel @@ -38,6 +40,16 @@ export type EveryTextChannelTypes = | GroupDMChannel | ThreadChannel +export type EveryGuildThreadAvailableChannelTypes = + | GuildTextChannel + | NewsChannel + | GuildForumChannel + +export type EveryGuildThreadAvailableChannelPayloadTypes = + | GuildTextChannelPayload + | GuildNewsChannelPayload + | GuildForumChannelPayload + export type EveryTextChannelPayloadTypes = | TextChannelPayload | GuildNewsChannelPayload @@ -54,6 +66,7 @@ export type EveryChannelTypes = | VoiceChannel | StageVoiceChannel | EveryTextChannelTypes + | EveryGuildThreadAvailableChannelTypes export type EveryChannelPayloadTypes = | ChannelPayload @@ -61,6 +74,7 @@ export type EveryChannelPayloadTypes = | GuildVoiceChannelPayload | GuildStageChannelPayload | EveryTextChannelPayloadTypes + | EveryGuildThreadAvailableChannelPayloadTypes /** Get appropriate Channel structure by its type */ const getChannelByType = ( @@ -115,6 +129,14 @@ const getChannelByType = ( if (guild === undefined) throw new Error('No Guild was provided to construct Channel') return new ThreadChannel(client, data as ThreadChannelPayload, guild) + case ChannelTypes.GUILD_FORUM: + if (guild === undefined) + throw new Error('No Guild was provided to construct Channel') + return new GuildForumChannel( + client, + data as GuildForumChannelPayload, + guild + ) } } From 5cc3c25f1dbe8c3db1064ef4f580566e80873408 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 11 Oct 2022 10:54:57 +0900 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20fix:=20change=20e?= =?UTF-8?q?ndpoint=20and=20datas=20to=20fit=20with=20the=20new=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/managers/channelThreads.ts | 8 +- src/rest/endpoints.ts | 17 +++- src/structures/guildForumChannel.ts | 89 ++++++++++++++++++- src/structures/guildTextChannel.ts | 7 -- src/structures/guildThreadAvailableChannel.ts | 38 ++++++-- src/structures/message.ts | 2 +- src/types/channel.ts | 15 +++- 7 files changed, 153 insertions(+), 23 deletions(-) diff --git a/src/managers/channelThreads.ts b/src/managers/channelThreads.ts index 270be0e8..96a2389d 100644 --- a/src/managers/channelThreads.ts +++ b/src/managers/channelThreads.ts @@ -6,9 +6,11 @@ import type { ThreadChannel, ThreadMember } from '../structures/threadChannel.ts' -import type { CreateThreadOptions } from '../structures/guildTextChannel.ts' import type { BaseManager, Message } from '../../mod.ts' -import type { GuildThreadAvailableChannel } from '../structures/guildThreadAvailableChannel.ts' +import type { + CreateThreadOptions, + GuildThreadAvailableChannel +} from '../structures/guildThreadAvailableChannel.ts' export class ChannelThreadsManager extends BaseChildManager< ThreadChannelPayload, @@ -58,7 +60,7 @@ export class ChannelThreadsManager extends BaseChildManager< async start( options: CreateThreadOptions, - message: string | Message + message?: string | Message ): Promise { return this.channel.startThread(options, message) } diff --git a/src/rest/endpoints.ts b/src/rest/endpoints.ts index 152dbfd9..fcbb60bf 100644 --- a/src/rest/endpoints.ts +++ b/src/rest/endpoints.ts @@ -1366,9 +1366,9 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding } /** - * Creates a new public thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. + * Creates a new thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. */ - async startPublicThread( + async startPublicThreadFromMessage( channelId: string, messageId: string, payload: CreateThreadPayload @@ -1379,6 +1379,17 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding ) } + /** + * Creates a new thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. + */ + async startThreadWithoutMessage( + channelId: string, + payload: CreateThreadPayload + ): Promise { + return this.rest.post(`/channels/${channelId}/threads`, payload) + } + + // Exist for backwards compatibility /** * Creates a new private thread. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. */ @@ -1386,7 +1397,7 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding channelId: string, payload: CreateThreadPayload ): Promise { - return this.rest.post(`/channels/${channelId}/threads`, payload) + return this.startThreadWithoutMessage(channelId, payload) } /** diff --git a/src/structures/guildForumChannel.ts b/src/structures/guildForumChannel.ts index 4b581126..5793453e 100644 --- a/src/structures/guildForumChannel.ts +++ b/src/structures/guildForumChannel.ts @@ -1,15 +1,32 @@ import { Client } from '../client/client.ts' +import type { AllMessageOptions } from '../managers/channels.ts' import { + CreateThreadInForumPayload, GuildForumChannelPayload, GuildForumSortOrderTypes, GuildForumTagPayload, ModifyGuildForumChannelOption, - ModifyGuildForumChannelPayload + ModifyGuildForumChannelPayload, + ThreadChannelPayload } from '../types/channel.ts' import { CHANNEL } from '../types/endpoint.ts' +import { transformComponent } from '../utils/components.ts' +import { Embed } from './embed.ts' import { Emoji } from './emoji.ts' import { Guild } from './guild.ts' import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts' +import { Message } from './message.ts' +import { ThreadChannel } from './threadChannel.ts' + +export interface CreateThreadInForumOptions { + /** 2-100 character channel name */ + name: string + /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ + autoArchiveDuration?: number + slowmode?: number | null + message: string | AllMessageOptions + appliedTags?: string[] | GuildForumTag[] +} export class GuildForumTag { id!: string @@ -100,4 +117,74 @@ export class GuildForumChannel extends GuildThreadAvailableChannel { return new GuildForumChannel(this.client, resp, this.guild) } + + override async startThread( + options: CreateThreadInForumOptions, + message?: string | AllMessageOptions | Message + ): Promise { + if (options.message !== undefined) { + message = options.message + } + if (message instanceof Message) { + message = { + content: message.content, + embeds: message.embeds.map((embed) => new Embed(embed)), + components: message.components + } + } else if (message instanceof Embed) { + message = { + embed: message + } + } else if (Array.isArray(message)) { + message = { + embeds: message + } + } else if (typeof message === 'string') { + message = { + content: message + } + } + + const messageObject = { + content: message?.content, + embed: message?.embed, + embeds: message?.embeds, + file: message?.file, + files: message?.files, + allowed_mentions: message?.allowedMentions, + components: + message?.components !== undefined + ? typeof message.components === 'function' + ? message.components() + : transformComponent(message.components) + : undefined + } + + if ( + messageObject.content === undefined && + messageObject.embed === undefined + ) { + messageObject.content = '' + } + + const body: CreateThreadInForumPayload = { + name: options.name, + auto_archive_duration: options.autoArchiveDuration, + rate_limit_per_user: options.slowmode, + message: messageObject, + applied_tags: options.appliedTags?.map((tag) => { + if (tag instanceof GuildForumTag) { + return tag.id + } + return tag + }) + } + + const resp: ThreadChannelPayload = await this.client.rest.api.channels[ + this.id + ].threads.post(body) + const thread = new ThreadChannel(this.client, resp, this.guild) + this.threads.set(thread.id, resp) + return thread + } } diff --git a/src/structures/guildTextChannel.ts b/src/structures/guildTextChannel.ts index 88f7547a..7e54225a 100644 --- a/src/structures/guildTextChannel.ts +++ b/src/structures/guildTextChannel.ts @@ -12,13 +12,6 @@ import { CHANNEL } from '../types/endpoint.ts' import type { Message } from './message.ts' import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts' -export interface CreateThreadOptions { - /** 2-100 character channel name */ - name: string - /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - autoArchiveDuration: number -} - /** Represents a Text Channel but in a Guild */ export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) { constructor( diff --git a/src/structures/guildThreadAvailableChannel.ts b/src/structures/guildThreadAvailableChannel.ts index b5197025..da9e2d9d 100644 --- a/src/structures/guildThreadAvailableChannel.ts +++ b/src/structures/guildThreadAvailableChannel.ts @@ -1,6 +1,8 @@ +import { AllMessageOptions } from '../../mod.ts' import { Client } from '../client/client.ts' import { ChannelThreadsManager } from '../managers/channelThreads.ts' import { + ChannelTypes, GuildThreadAvailableChannelPayload, ModifyGuildThreadAvailableChannelOption, ModifyGuildThreadAvailableChannelPayload @@ -8,10 +10,19 @@ import { import { CHANNEL } from '../types/endpoint.ts' import { GuildChannel } from './channel.ts' import { Guild } from './guild.ts' -import { CreateThreadOptions } from './guildTextChannel.ts' import { Message } from './message.ts' import { ThreadChannel, ThreadMember } from './threadChannel.ts' +export interface CreateThreadOptions { + /** 2-100 character channel name */ + name: string + /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ + autoArchiveDuration?: number + slowmode?: number | null + type?: ChannelTypes + invitable?: boolean +} + export class GuildThreadAvailableChannel extends GuildChannel { topic?: string slowmode!: number @@ -91,13 +102,26 @@ export class GuildThreadAvailableChannel extends GuildChannel { async startThread( options: CreateThreadOptions, - message: Message | string + message?: Message | string ): Promise { - const payload = await this.client.rest.endpoints.startPublicThread( - this.id, - typeof message === 'string' ? message : message.id, - { name: options.name, auto_archive_duration: options.autoArchiveDuration } - ) + const payload = + message !== undefined + ? await this.client.rest.endpoints.startPublicThreadFromMessage( + this.id, + typeof message === 'string' ? message : message.id, + { + name: options.name, + auto_archive_duration: options.autoArchiveDuration, + rate_limit_per_user: options.slowmode + } + ) + : await this.client.rest.endpoints.startThreadWithoutMessage(this.id, { + name: options.name, + auto_archive_duration: options.autoArchiveDuration, + rate_limit_per_user: options.slowmode, + invitable: options.invitable, + type: options.type + }) await this.client.channels.set(payload.id, payload) return (await this.client.channels.get(payload.id))! } diff --git a/src/structures/message.ts b/src/structures/message.ts index 3e6ce89b..eb35d034 100644 --- a/src/structures/message.ts +++ b/src/structures/message.ts @@ -16,7 +16,6 @@ import { CHANNEL_MESSAGE } from '../types/endpoint.ts' import { MessageMentions } from './messageMentions.ts' import type { TextChannel } from './textChannel.ts' import type { - CreateThreadOptions, GuildTextBasedChannel, GuildTextChannel } from './guildTextChannel.ts' @@ -29,6 +28,7 @@ import { encodeText } from '../utils/encoding.ts' import { MessageComponentData } from '../types/messageComponents.ts' import { transformComponentPayload } from '../utils/components.ts' import type { ThreadChannel } from './threadChannel.ts' +import { CreateThreadOptions } from './guildThreadAvailableChannel.ts' type AllMessageOptions = MessageOptions | Embed diff --git a/src/types/channel.ts b/src/types/channel.ts index ee90c8b5..1732cacc 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -603,5 +603,18 @@ export interface CreateThreadPayload { /** 2-100 character channel name */ name: string /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - auto_archive_duration: number + auto_archive_duration?: number + rate_limit_per_user?: number | null + type?: ChannelTypes + invitable?: boolean +} + +export interface CreateThreadInForumPayload { + /** 2-100 character channel name */ + name: string + /** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ + auto_archive_duration?: number + rate_limit_per_user?: number | null + message: CreateMessagePayload + applied_tags?: string[] } From 7611659556e1c9b2fc996129ecdbf4fb901714f9 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 11 Oct 2022 10:55:31 +0900 Subject: [PATCH 13/15] =?UTF-8?q?=E2=9C=85=20feat:=20add=20forum=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/forum.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/forum.ts diff --git a/test/forum.ts b/test/forum.ts new file mode 100644 index 00000000..f71ac945 --- /dev/null +++ b/test/forum.ts @@ -0,0 +1,28 @@ +import { Client, GuildForumChannel, Intents } from '../mod.ts' +// import type { } from '../mod.ts' +import { TOKEN } from './config.ts' + +const client = new Client() + +client.on('ready', async () => { + const guild = await client.guilds.resolve('GUILD_ID') + if (guild) { + console.log('found guild') + const channel = await guild.channels.resolve('CHANNEL_ID') + if (channel && channel instanceof GuildForumChannel) { + console.log('found channel') + const threads = await channel.threads.array() + console.log(threads) + const thread = await channel.startThread({ + name: 'also test', + autoArchiveDuration: 60, + message: { + content: 'test' + } + }) + thread.send('it works') + } + } +}) + +client.connect(TOKEN, Intents.NonPrivileged) From b4926f31038027219533adf06d311a03c261d6a8 Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Tue, 11 Oct 2022 11:03:59 +0900 Subject: [PATCH 14/15] =?UTF-8?q?=F0=9F=9A=A8=20fix:=20lint=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rest/endpoints.ts | 2 +- src/structures/guildForumChannel.ts | 15 ++++++++------- src/structures/guildThreadAvailableChannel.ts | 1 - src/utils/permissions.ts | 2 ++ test/forum.ts | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rest/endpoints.ts b/src/rest/endpoints.ts index fcbb60bf..abba0f93 100644 --- a/src/rest/endpoints.ts +++ b/src/rest/endpoints.ts @@ -1397,7 +1397,7 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding channelId: string, payload: CreateThreadPayload ): Promise { - return this.startThreadWithoutMessage(channelId, payload) + return await this.startThreadWithoutMessage(channelId, payload) } /** diff --git a/src/structures/guildForumChannel.ts b/src/structures/guildForumChannel.ts index 5793453e..c063ea33 100644 --- a/src/structures/guildForumChannel.ts +++ b/src/structures/guildForumChannel.ts @@ -39,7 +39,7 @@ export class GuildForumTag { this.readFromData(data) } - readFromData(data: GuildForumTagPayload) { + readFromData(data: GuildForumTagPayload): void { this.id = data.id ?? this.id this.name = data.name ?? this.name this.moderated = data.moderated ?? this.moderated @@ -63,12 +63,13 @@ export class GuildForumChannel extends GuildThreadAvailableChannel { this.availableTags = data.available_tags?.map((tag) => new GuildForumTag(tag)) ?? this.availableTags - this.defaultReactionEmoji = data.default_reaction_emoji - ? new Emoji(this.client, { - id: data.default_reaction_emoji.emoji_id, - name: data.default_reaction_emoji.emoji_name - }) - : this.defaultReactionEmoji + this.defaultReactionEmoji = + data.default_reaction_emoji !== null + ? new Emoji(this.client, { + id: data.default_reaction_emoji.emoji_id, + name: data.default_reaction_emoji.emoji_name + }) + : this.defaultReactionEmoji this.defaultSortOrder = data.default_sort_order ?? this.defaultSortOrder } diff --git a/src/structures/guildThreadAvailableChannel.ts b/src/structures/guildThreadAvailableChannel.ts index da9e2d9d..dc7a2ef4 100644 --- a/src/structures/guildThreadAvailableChannel.ts +++ b/src/structures/guildThreadAvailableChannel.ts @@ -1,4 +1,3 @@ -import { AllMessageOptions } from '../../mod.ts' import { Client } from '../client/client.ts' import { ChannelThreadsManager } from '../managers/channelThreads.ts' import { diff --git a/src/utils/permissions.ts b/src/utils/permissions.ts index 91df3b6e..b4514b19 100644 --- a/src/utils/permissions.ts +++ b/src/utils/permissions.ts @@ -18,6 +18,7 @@ export class Permissions extends BitField { any(permission: PermissionResolvable, checkAdmin = true): boolean { return ( + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions (checkAdmin && super.has(this.flags().ADMINISTRATOR)) || super.any(permission) ) @@ -25,6 +26,7 @@ export class Permissions extends BitField { has(permission: PermissionResolvable, checkAdmin = true): boolean { return ( + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions (checkAdmin && super.has(this.flags().ADMINISTRATOR)) || super.has(permission) ) diff --git a/test/forum.ts b/test/forum.ts index f71ac945..86a6f50e 100644 --- a/test/forum.ts +++ b/test/forum.ts @@ -6,10 +6,10 @@ const client = new Client() client.on('ready', async () => { const guild = await client.guilds.resolve('GUILD_ID') - if (guild) { + if (guild !== undefined) { console.log('found guild') const channel = await guild.channels.resolve('CHANNEL_ID') - if (channel && channel instanceof GuildForumChannel) { + if (channel !== undefined && channel instanceof GuildForumChannel) { console.log('found channel') const threads = await channel.threads.array() console.log(threads) From 304e9c4262efb3e66f2b45b504a99b41f4a29dee Mon Sep 17 00:00:00 2001 From: Helloyunho Date: Mon, 17 Oct 2022 21:15:19 +0900 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20fix:=20keep=20the?= =?UTF-8?q?=20backwards=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rest/endpoints.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/rest/endpoints.ts b/src/rest/endpoints.ts index abba0f93..1ed55dea 100644 --- a/src/rest/endpoints.ts +++ b/src/rest/endpoints.ts @@ -1379,6 +1379,22 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding ) } + // Exist for backwards compatibility + /** + * Creates a new public thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. + */ + async startPublicThread( + channelId: string, + messageId: string, + payload: CreateThreadPayload + ): Promise { + return await this.startPublicThreadFromMessage( + channelId, + messageId, + payload + ) + } + /** * Creates a new thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event. */