diff --git a/src/handlers/channels/THREAD_MEMBER_UPDATE.ts b/src/handlers/channels/THREAD_MEMBER_UPDATE.ts index 8e242b7e3..8233f5f11 100644 --- a/src/handlers/channels/THREAD_MEMBER_UPDATE.ts +++ b/src/handlers/channels/THREAD_MEMBER_UPDATE.ts @@ -9,6 +9,9 @@ export async function handleThreadMemberUpdate(data: DiscordGatewayPayload) { const thread = await cacheHandlers.get("threads", snowflakeToBigint(payload.id)); if (!thread) return; + thread.botIsMember = true; + await cacheHandlers.set("threads", thread.id, thread); + const member = { ...payload, id: snowflakeToBigint(payload.id), diff --git a/src/helpers/channels/threads/get_thread_members.ts b/src/helpers/channels/threads/get_thread_members.ts index cd91c9df3..1fc49a0be 100644 --- a/src/helpers/channels/threads/get_thread_members.ts +++ b/src/helpers/channels/threads/get_thread_members.ts @@ -1,36 +1,24 @@ import { cacheHandlers } from "../../../cache.ts"; import { rest } from "../../../rest/rest.ts"; -import { ChannelTypes } from "../../../types/channels/channel_types.ts"; import { Errors } from "../../../types/discordeno/errors.ts"; import { DiscordGatewayIntents } from "../../../types/gateway/gateway_intents.ts"; import { endpoints } from "../../../util/constants.ts"; import { botHasChannelPermissions } from "../../../util/permissions.ts"; import { ws } from "../../../ws/ws.ts"; -/** Returns array of thread members objects that are members of the thread. */ -export async function getThreadMembers(channelId: bigint) { - if (!(ws.identifyPayload.intents & DiscordGatewayIntents.GuildMembers)) { +/** Returns thread members objects that are members of the thread. */ +export async function getThreadMembers(threadId: bigint) { + // Check if intents is not 0 as proxy ws won't set intents in other instances + if (ws.identifyPayload.intents && !(ws.identifyPayload.intents & DiscordGatewayIntents.GuildMembers)) { throw new Error(Errors.MISSING_INTENT_GUILD_MEMBERS); } - const channel = await cacheHandlers.get("channels", channelId); - if (channel) { - if ( - ![ChannelTypes.GuildNewsThread, ChannelTypes.GuildPivateThread, ChannelTypes.GuildPublicThread].includes( - channel.type - ) - ) { - throw new Error(Errors.NOT_A_THREAD_CHANNEL); - } - - if ( - channel.type === ChannelTypes.GuildPivateThread && - !(await botHasChannelPermissions(channel, ["MANAGE_THREADS"])) && - !channel.member - ) + const thread = await cacheHandlers.get("threads", threadId); + if (thread?.isPrivate) { + const channel = await cacheHandlers.get("channels", thread.channelId); + if (channel && !(await botHasChannelPermissions(channel, ["MANAGE_THREADS"])) && !thread.botIsMember) throw new Error(Errors.CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD); } - // TODO: v12 map the result to a nice collection - return await rest.runMethod("get", endpoints.THREAD_MEMBERS(channelId)); + return await rest.runMethod("get", endpoints.THREAD_MEMBERS(threadId)); } diff --git a/src/helpers/channels/threads/start_private_thread.ts b/src/helpers/channels/threads/start_private_thread.ts index f43078459..48c07007d 100644 --- a/src/helpers/channels/threads/start_private_thread.ts +++ b/src/helpers/channels/threads/start_private_thread.ts @@ -15,7 +15,7 @@ export async function startPrivateThread(channelId: bigint, options: StartThread if (channel.isNewsChannel) throw new Error(Errors.GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS); - await requireBotChannelPermissions(channel, ["USE_PRIVATE_THREADS"]); + await requireBotChannelPermissions(channel, ["SEND_MESSAGES", "USE_PRIVATE_THREADS"]); } return channelToThread(await rest.runMethod("post", endpoints.THREAD_START_PRIVATE(channelId), snakelize(options))); diff --git a/src/helpers/channels/threads/start_thread.ts b/src/helpers/channels/threads/start_thread.ts index 7d76ab1e5..35e9b4ce5 100644 --- a/src/helpers/channels/threads/start_thread.ts +++ b/src/helpers/channels/threads/start_thread.ts @@ -14,7 +14,7 @@ export async function startThread(channelId: bigint, options: StartThread & { me throw new Error(Errors.INVALID_THREAD_PARENT_CHANNEL_TYPE); } - await requireBotChannelPermissions(channel, ["USE_PUBLIC_THREADS"]); + await requireBotChannelPermissions(channel, ["SEND_MESSAGES", "USE_PUBLIC_THREADS"]); } return await rest.runMethod( diff --git a/src/types/discordeno/event_handlers.ts b/src/types/discordeno/event_handlers.ts index fd152c574..3562b1c7b 100644 --- a/src/types/discordeno/event_handlers.ts +++ b/src/types/discordeno/event_handlers.ts @@ -3,7 +3,7 @@ import type { DiscordenoGuild } from "../../structures/guild.ts"; import type { DiscordenoMember } from "../../structures/member.ts"; import type { DiscordenoMessage } from "../../structures/message.ts"; import type { DiscordenoRole } from "../../structures/role.ts"; -import { Thread } from "../../util/transformers/channel_to_thread.ts"; +import { DiscordenoThread } from "../../util/transformers/channel_to_thread.ts"; import type { Collection } from "../../util/collection.ts"; import type { PresenceUpdate } from "../activity/presence_update.ts"; import type { StageInstance } from "../channels/stage_instance.ts"; @@ -129,17 +129,17 @@ export type EventHandlersDefinitions = { /** Sent when a Stage instance has been updated. */ stageInstanceUpdate: [instance: StageInstance]; /** Sent when a thread is created */ - threadCreate: [thread: Thread]; + threadCreate: [thread: DiscordenoThread]; /** Sent when a thread is updated */ - threadUpdate: [thread: Thread, oldThread: Thread]; + threadUpdate: [thread: DiscordenoThread, oldThread: DiscordenoThread]; /** Sent when the bot gains access to threads */ - threadListSync: [threads: Collection, members: ThreadMemberModified[], guildId: bigint]; + threadListSync: [threads: Collection, members: ThreadMemberModified[], guildId: bigint]; /** Sent when the current users thread member is updated */ - threadMemberUpdate: [threadMember: ThreadMemberModified, thread: Thread]; + threadMemberUpdate: [threadMember: ThreadMemberModified, thread: DiscordenoThread]; /** Sent when anyone is added to or removed from a thread */ threadMembersUpdate: [update: ThreadMembersUpdateModified]; /** Sent when a thread is deleted */ - threadDelete: [thread: Thread]; + threadDelete: [thread: DiscordenoThread]; /** Sent when a user starts typing in a channel. */ typingStart: [data: TypingStart]; /** Sent when a user joins a voice channel */ diff --git a/src/util/transformers/channel_to_thread.ts b/src/util/transformers/channel_to_thread.ts index bb9612011..4d6c2c1bf 100644 --- a/src/util/transformers/channel_to_thread.ts +++ b/src/util/transformers/channel_to_thread.ts @@ -1,4 +1,5 @@ import { Channel } from "../../types/channels/channel.ts"; +import { DiscordChannelTypes } from "../../types/channels/channel_types.ts"; import { snowflakeToBigint } from "../bigint.ts"; import { createNewProp } from "../utils.ts"; @@ -16,9 +17,16 @@ const baseThread: Partial = { get locked() { return Boolean(this.bitfield! & threadToggles.locked); }, + get isPrivate() { + return this.type === DiscordChannelTypes.GuildPrivateThread; + }, + get isPublic() { + return !this.isPrivate; + }, toJSON() { return { id: this.id?.toString(), + type: this.type, channelId: this.channelId?.toString(), memberCount: this.memberCount, messageCount: this.messageCount, @@ -39,6 +47,7 @@ export function channelToThread(channel: Channel) { return Object.create(baseThread, { id: createNewProp(snowflakeToBigint(channel.id)), + type: createNewProp(channel.type), channelId: createNewProp(snowflakeToBigint(channel.parentId!)), memberCount: createNewProp(channel.memberCount), messageCount: createNewProp(channel.messageCount), @@ -50,12 +59,17 @@ export function channelToThread(channel: Channel) { ), autoArchiveDuration: createNewProp(channel.threadMetadata?.autoArchiveDuration || 0), bitfield: createNewProp(bitfield), - ownerId: createNewProp(snowflakeToBigint(channel.ownerId!)) + ownerId: createNewProp(snowflakeToBigint(channel.ownerId!)), + botIsMember: createNewProp(Boolean(channel.member)) }); } export interface Thread { id: string; + type: + | DiscordChannelTypes.GuildNewsThread + | DiscordChannelTypes.GuildPublicThread + | DiscordChannelTypes.GuildPrivateThread; channelId: string; memberCount: number; messageCount: number; @@ -65,10 +79,15 @@ export interface Thread { archived: boolean; locked: boolean; ownerId: string; + botIsMember: boolean; } export interface DiscordenoThread { id: bigint; + type: + | DiscordChannelTypes.GuildNewsThread + | DiscordChannelTypes.GuildPublicThread + | DiscordChannelTypes.GuildPrivateThread; channelId: bigint; memberCount: number; messageCount: number; @@ -79,5 +98,8 @@ export interface DiscordenoThread { locked: boolean; bitfield: bigint; ownerId: bigint; + isPrivate: boolean; + isPublic: boolean; + botIsMember: boolean; toJSON(): Thread; }