From 23bf320026c425e1abb7a2c0eb099dc7d1611f1f Mon Sep 17 00:00:00 2001 From: iiFDCT Date: Tue, 28 Sep 2021 01:55:01 +0100 Subject: [PATCH] Squashed commit of the following: commit 65cb0ab22a74691747a4d24e893a7b3049e35ac1 Author: bsian03 Date: Fri Sep 3 02:31:17 2021 +0100 Actually return the member commit fbcbb25c1cc3a199523458c3c9cc5199df939ffb Author: bsian03 Date: Fri Sep 3 02:14:48 2021 +0100 Add thread channel ID to guild map on thread update commit b861556b4c9fbc5e3fedab63d0a637518dd07d65 Merge: 69d5acc daedb2b Author: bsian03 Date: Fri Sep 3 01:51:07 2021 +0100 Merge branch 'dev' into v9 commit 69d5acc341d3295c559c72de8b7c2e1ff08412eb Author: bsian03 Date: Fri Sep 3 01:44:18 2021 +0100 No one saw this commit c09be80304ff08a31274205f608b35ea1288a4b0 Author: bsian03 Date: Fri Sep 3 01:43:56 2021 +0100 This is a better fix commit 3e3f6239aea78f9e8212d7dfeddc3a882202ccf7 Author: bsian03 Date: Fri Sep 3 01:42:13 2021 +0100 Fix not sending newly cached thread channel commit daedb2b416d0d3f880668ebed719477d9be59b2d Author: Donovan Daniels Date: Thu Sep 2 16:40:24 2021 -0500 Sticker Sending (#1252) Co-Authored-By: Catboy Co-authored-by: bsian03 commit 6e4990170b5c98f5678091d14fed38336c1c0d56 Merge: a8d9a6a e357991 Author: bsian03 Date: Tue Aug 31 22:39:20 2021 +0100 Merge branch 'dev' into v9 commit a8d9a6ae522e2751fed94c7fdc196d977bb01f64 Author: bsian03 Date: Tue Aug 31 22:33:46 2021 +0100 Correct permission name commit 9bb0a17f8f14ad3d942897146c81cf3f47db7802 Author: bsian03 Date: Tue Aug 31 22:32:14 2021 +0100 Thread metadata locked guaranteed commit e357991c686f900000450dc24834f3afc56b0943 Author: 5antos <66580910+5antos@users.noreply.github.com> Date: Sun Aug 29 23:18:45 2021 +0100 Enhancements for VoiceChannel join methods (#1195) Co-authored-by: Almeida Co-authored-by: bsian03 commit f3a44356439ed626d31c65ac68b1a429e42f1387 Author: bsian03 Date: Sat Aug 28 23:59:50 2021 +0100 Documentation fixes commit 6af124d365ea2b542f42ca72e9791d95290df054 Merge: 890465d 53da0d5 Author: Bsian Date: Sun Aug 22 12:29:24 2021 +0100 Merge branch 'dev' of https://github.com/abalabahaha/eris into v9 commit 890465d7112d00b073ff934f211db469c24649db Author: Bsian Date: Fri Aug 20 19:27:03 2021 +0100 added thread member presence is nullable commit afad66e46e41493e3d09d02c8811c4eb92fb6953 Author: Bsian Date: Wed Aug 18 00:25:54 2021 +0100 Send messages in threads permission commit 134f5b9c19eeabb199997fa84d9909ae4209565c Author: Bsian Date: Wed Aug 18 00:14:53 2021 +0100 PrivateThread invitable commit 000732b2e1f13636a61bc648896134ea3c908713 Author: Bsian Date: Tue Aug 17 21:24:58 2021 +0100 Seem to have somehow forgotten to remove this commit b7b7fba58487d26a2785cd226d261b84e687bee8 Author: Bsian Date: Wed Aug 11 14:48:58 2021 +0100 getActiveThreads dep to getActiveGuildThreads commit bee0cfef0c2f4a395a18b2c23847a065f981c092 Author: Bsian Date: Wed Aug 11 14:16:30 2021 +0100 Doc updates r.e. v10 commit a0a75f4300bf54382d066d2ea38037d4fec48986 Author: Bsian Date: Wed Aug 11 14:10:47 2021 +0100 Lint commit f85da811812aacf45d1a39093e8090dfbc1d3fb9 Author: Bsian Date: Wed Aug 11 14:10:33 2021 +0100 Update member cache on threadMembersUpdate commit 098e04c4a3127b4167f3495c2265d7f20c79cff0 Author: Bsian Date: Wed Aug 11 13:51:20 2021 +0100 Fix caching IDs for threadMembersUpdate commit 4e86f70fc9aca45254b1cbec743dcc9c745268b4 Author: Bsian Date: Wed Aug 11 13:33:57 2021 +0100 Support threads in audit log commit 3778a27699ca2a6f18598806fd4bbba842adcd7c Author: Bsian Date: Wed Aug 11 13:18:46 2021 +0100 Correct defaultAutoArchiveDuration prop names commit 4e3a2f604c16991ebd277656e3a6af332615cad6 Author: Bsian Date: Wed Aug 4 11:58:37 2021 +0100 Lint commit 9f04f8d73c25c51c40511fe2c7898e2be38ee96e Author: Bsian Date: Tue Aug 3 21:48:10 2021 +0100 Cache all incoming THREAD_UPDATEs commit 24b04d717687e92fdfed2daed0580f4828a316ed Author: Bsian Date: Tue Aug 3 16:19:07 2021 +0100 Circular import fix commit 1fad0e46bf4ab9155666ba710f9449ea069c5fc3 Author: Bsian Date: Tue Aug 3 15:44:01 2021 +0100 Fix thread parent ID docs commit e84743ac03d303917000900a4ded54736b01d3e4 Author: Bsian Date: Tue Aug 3 15:23:26 2021 +0100 Update stage instance members properly commit b5a0a8879f726722c59f8f39b14d62af8b94843b Author: Bsian Date: Tue Aug 3 15:20:10 2021 +0100 Correct typings (message ternary checks) commit 448dcb0c436f292c623a882f27365a0ce99d480e Author: Bsian Date: Tue Aug 3 15:13:44 2021 +0100 Typings fixes (class implementations) commit 083f94ebdaa3d1f5ee36adce1b4c6fc002bb23f6 Author: Bsian Date: Mon Aug 2 17:43:01 2021 +0100 Fix permissionsOf when run in stage channels commit 29a5ca877fbe9f8e5107877cb22883241d654952 Author: Bsian Date: Sun Aug 1 15:36:55 2021 +0100 Correct getActiveThreads commit 6aaae359f30bf2a690bdc1dade8b0934df62e56a Author: bsian03 Date: Sun Aug 1 14:46:49 2021 +0100 Lint Co-authored-by: iiFDCT <35053522+iiFDCT@users.noreply.github.com> commit ffb55481294bc44917f118d9a87f9f9b5c7788e7 Author: bsian03 Date: Fri Jul 30 23:54:48 2021 +0100 also for 18 commit b499354bf104c6e24ae9bd1ad1f1c3f8d46de57d Author: bsian03 Date: Fri Jul 30 22:59:02 2021 +0100 Add case for type 21 commit b75a28ebdb314c24a8448663a325dfd29b7dfbec Author: Bsian Date: Tue Jul 13 12:07:26 2021 +0100 Prefix client prop commit 9fad4dad5708d929ba84ea3544f3f36341b4a4d7 Author: Donovan Daniels Date: Mon Jul 12 05:04:35 2021 -0500 Fix typings for creating thread messages (#15) commit 3a1b26a4b32989b02c1b170a17bbf4a0bdc90f3d Author: Bsian Date: Fri Jul 2 13:09:18 2021 +0100 Pass client in param commit f8f77c6f79cb275c42138415ab88648136e03f6e Author: Bsian Date: Wed Jun 30 09:20:15 2021 +0100 Consistency commit 2b2391826af4c15054689ea181682c43f34e0037 Author: Bsian Date: Wed Jun 30 09:16:23 2021 +0100 Support editing thread default archive duration commit 5147bda8bb521e895ed903d99846b7c770902aed Author: Bsian Date: Wed Jun 30 09:05:12 2021 +0100 Support stage instance invites commit 83bbbdf7a4be83410974307381f195b13ebcaaf1 Author: Bsian Date: Wed Jun 30 00:18:53 2021 +0100 Update threads to documentation commit 54376a0da9f933d26d2a675947270c8ef2752a65 Author: Bsian Date: Wed Jun 30 00:06:03 2021 +0100 Support stage instances in audit log commit 3d57bef633b27b4da2b0200444771a91486a199c Author: Bsian Date: Tue Jun 29 23:45:02 2021 +0100 Fix getChannel using channels prop when getting thread channel commit 7678cdec5b6b4b8f52f4597ec37d5dcddb9d0f4f Author: Bsian Date: Tue Jun 29 23:44:35 2021 +0100 No idea how this trailing whitespace got here commit 9bfe4220ce6ba08f9354b640cd027f1e46009d41 Author: Bsian Date: Tue Jun 29 23:42:59 2021 +0100 Cover channel types in Channel.from commit 34a6623e6ea170749870eccd09e545c15b495b9d Author: Bsian Date: Tue Jun 29 23:42:48 2021 +0100 Fix incorrectly named constructor param commit b405958adfd8c66e982d42fc727dc608fa7b6f0d Author: Bsian Date: Tue Jun 29 23:35:39 2021 +0100 Add missing functionality methods commit 5184e454ee2cbbc7a839cc2a9e436455ced048b7 Author: Bsian Date: Tue Jun 29 22:55:02 2021 +0100 Correct documentation commit 289b83478dc6f4006516be5fce2aead20eca6c44 Author: Bsian Date: Wed Jun 23 22:40:00 2021 +0100 Thread without message type commit 6709477e1e1586a59cb27056503775786faa7374 Author: Bsian Date: Wed Jun 23 22:33:49 2021 +0100 Update method names and docs to match Discord commit a3b0fea7a032dce83cf8335ec37ff313739d726f Author: Bsian Date: Wed Jun 23 22:05:36 2021 +0100 Add channel types to thread channel classes commit 6fcfcc703b1ae7f79cd2fa4ca093f4396438038b Author: Bsian Date: Sat Jun 5 01:02:45 2021 +0100 Remove Guild#region commit b6f93de3439a926ce0b38da41141bf7e0b5176df Author: Bsian Date: Thu May 27 16:55:51 2021 +0100 Stage discovery commit 239f48db246ded429667235c6e3582b38fbe6580 Author: Bsian Date: Thu May 27 14:09:54 2021 +0100 Stage instance caching and gateway events commit 0de18ca52e731df8238dd201b822b575cb1da8da Author: Bsian Date: Thu May 27 13:49:25 2021 +0100 Fix typo commit 47f8d20ff27f0b4033dfeb44476d550f424c4488 Author: Bsian Date: Thu May 27 13:37:30 2021 +0100 Update Webhook interface (interaction webhooks) commit 7b0edbbba79e6d60425045c8258abefa9b71a8d2 Author: Bsian Date: Thu May 27 10:40:50 2021 +0100 GuildMember joinedAt can be null commit d1186d4701c737d7c65008e682ede42d8c56700c Author: Bsian Date: Thu May 27 10:37:01 2021 +0100 Expose Message#applicationID commit 1f20e386808519f044dcf6034c3c6f8f8aa4c82b Author: Bsian Date: Thu May 27 10:33:12 2021 +0100 Expose stage instance commit c591b45b9a3957e123dc6853b7f357fba6dca71a Author: Bsian Date: Thu May 27 09:54:42 2021 +0100 Extra newline commit f8c11eab0fb8c42b259d0c8d56828fac0ea5ab14 Author: Bsian Date: Thu May 27 09:42:20 2021 +0100 Thread documentation clarification commit a78baca01240195d1849450accc6bbcc11035750 Author: Bsian Date: Thu May 27 09:34:10 2021 +0100 Support webhooks sending to thread channels commit c6a3fd03bddf5d2a582c148a77211a037a62e073 Author: Bsian Date: Thu May 27 09:19:33 2021 +0100 Support uploading new attachments on message edit commit e7a2401c1995c6da8eaa4af9cbc5c0efd894f3b6 Author: Bsian Date: Tue May 25 23:20:34 2021 +0100 Complete gateway events commit 7eb457c7d23a43efb27f8e2e124536408a97482d Author: Bsian Date: Tue May 25 23:14:38 2021 +0100 Doc fixes commit 8888a27430a92e136b1590809df63c18d7ea7409 Author: Bsian Date: Tue May 25 23:11:20 2021 +0100 Add ThreadMember cache commit 8a2bc9094a505e7d49abd7cc169f4c074e33f8e7 Author: Bsian Date: Tue May 25 23:10:29 2021 +0100 Discord messed up case commit 54ef7a49fa247f72fc42f9b620ee8052d0e4d722 Author: Bsian Date: Tue May 25 14:00:47 2021 +0100 Thread events (so far) commit b7ac7bd39870274e07294127e046a43c405a9172 Author: Bsian Date: Tue May 25 13:48:17 2021 +0100 Add support for stage channels in getChannel commit e7099fac6e41e7788017b6641496e02cef842e8e Author: Bsian Date: Tue May 25 10:44:10 2021 +0100 Doc update commit 1305ed6299336c54a8619e2178214a49dce8971c Author: Bsian Date: Tue May 25 10:27:10 2021 +0100 Documentation update commit 270e1f74f6817dbe89088cf90a545ffb705f4641 Author: Bsian Date: Tue May 25 10:24:17 2021 +0100 Add threads prop to guild commit 5d433aa898d3df0db026f2355f0333c37f374885 Author: Bsian Date: Tue May 25 10:10:53 2021 +0100 References for ThreadMember commit 552b7de89997a8f00315be10bf899fbedaf604ef Author: Bsian Date: Tue May 25 10:06:22 2021 +0100 ThreadMember class commit 713a43b4fb535fe7d46b6c084586ad35e616f265 Author: Bsian Date: Tue May 25 10:05:17 2021 +0100 Fix some stuff commit 22bf440bce08992fdb446387b125825430c98ffd Author: Bsian Date: Tue May 25 09:31:34 2021 +0100 Typings cleanup commit 7403f8d47b6c40c410a5d3e882d6d84d7751e890 Author: Bsian Date: Tue May 25 09:11:23 2021 +0100 New classes and methods commit 21e23b660e2d4d546741374a1b355e68aa61ceca Author: Bsian Date: Mon May 24 23:15:19 2021 +0100 v9 endpoints commit 90076d70ddf404e779fcf30a815e60817a1326b8 Author: Bsian Date: Mon May 24 22:10:26 2021 +0100 v9 Constants --- index.d.ts | 168 ++++++++++++++++++------- lib/Client.js | 67 ++++++---- lib/Constants.js | 19 ++- lib/gateway/Shard.js | 22 +++- lib/rest/Endpoints.js | 1 + lib/structures/Guild.js | 8 ++ lib/structures/GuildAuditLogEntry.js | 6 +- lib/structures/GuildChannel.js | 16 ++- lib/structures/Invite.js | 2 +- lib/structures/Message.js | 30 ++--- lib/structures/PrivateChannel.js | 1 + lib/structures/PrivateThreadChannel.js | 19 +++ lib/structures/TextChannel.js | 12 +- lib/structures/ThreadChannel.js | 3 +- lib/structures/ThreadMember.js | 2 + lib/structures/VoiceChannel.js | 2 + 16 files changed, 273 insertions(+), 105 deletions(-) diff --git a/index.d.ts b/index.d.ts index 485a9be76a..2051527d1e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,14 +20,15 @@ declare namespace Eris { // Channel type AnyChannel = AnyGuildChannel | PrivateChannel; type AnyGuildChannel = GuildTextableChannel | AnyVoiceChannel | CategoryChannel | StoreChannel; - type AnyThreadChannel = NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel; + type AnyThreadChannel = NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel | ThreadChannel; type AnyVoiceChannel = VoiceChannel | StageChannel; type ChannelTypes = Constants["ChannelTypes"][keyof Constants["ChannelTypes"]]; - type GuildTextableChannel = TextChannel | NewsChannel | AnyThreadChannel; + type GuildTextableChannel = TextChannel | NewsChannel; + type GuildTextableWithThread = GuildTextableChannel | AnyThreadChannel; type InviteChannel = InvitePartialChannel | Exclude; type PossiblyUncachedTextable = Textable | Uncached; type PossiblyUncachedTextableChannel = TextableChannel | Uncached; - type TextableChannel = (GuildTextable & GuildTextableChannel) | (Textable & PrivateChannel); + type TextableChannel = (GuildTextable & GuildTextableChannel) | (ThreadTextable & AnyThreadChannel) | (Textable & PrivateChannel); type VideoQualityMode = 1 | 2; // Command @@ -131,6 +132,7 @@ declare namespace Eris { autoArchiveDuration?: AutoArchiveDuration; defaultAutoArchiveDuration?: AutoArchiveDuration; icon?: string; + invitable?: boolean; locked?: boolean; name?: string; ownerID?: string; @@ -154,14 +156,14 @@ declare namespace Eris { createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; getWebhooks(): Promise; - purge(limit: number, filter?: (message: Message) => boolean, before?: string, after?: string, reason?: string): Promise; + purge(options: PurgeChannelOptions): Promise; removeMessageReactionEmoji(messageID: string, reaction: string): Promise; removeMessageReactions(messageID: string): Promise; sendTyping(): Promise; } interface PartialChannel { bitrate?: number; - id?: number; + id: string; name?: string; nsfw?: boolean; parent_id?: number; @@ -201,6 +203,16 @@ declare namespace Eris { unpinMessage(messageID: string): Promise; unsendMessage(messageID: string): Promise; } + // @ts-ignore ts(2430) - ThreadTextable can't properly extend Textable because of getMessageReaction deprecated overload + interface ThreadTextable extends Textable { + lastPinTimestamp?: number; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } interface WebhookData { channelID: string; guildID: string; @@ -563,6 +575,15 @@ declare namespace Eris { relationshipAdd: [relationship: Relationship]; relationshipRemove: [relationship: Relationship]; relationshipUpdate: [relationship: Relationship, oldRelationship: { type: number }]; + stageInstanceCreate: [stageInstance: StageInstance]; + stageInstanceDelete: [stageInstance: StageInstance]; + stageInstanceUpdate: [stageInstance: StageInstance, oldStageInstance: OldStageInstance | null]; + threadCreate: [channel: AnyThreadChannel]; + threadDelete: [channel: AnyThreadChannel]; + threadListSync: [guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]]; + threadMembersUpdate: [channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]]; + threadMemberUpdate: [channel: AnyThreadChannel, member: ThreadMember, oldMember: OldThreadMember]; + threadUpdate: [channel: AnyThreadChannel, oldChannel: OldThread | null]; typingStart: [channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member] | [channel: PrivateChannel | Uncached, user: User | Uncached, member: null]; unavailableGuildCreate: [guild: UnavailableGuild]; @@ -573,7 +594,7 @@ declare namespace Eris { voiceChannelSwitch: [member: Member, newChannel: AnyVoiceChannel, oldChannel: AnyVoiceChannel]; voiceStateUpdate: [member: Member, oldState: OldVoiceState]; warn: [message: string, id: number]; - webhooksUpdate: [data: WebhookData]; + webhooksUpdate: [data: WebhookData]; } interface ClientEvents extends EventListeners { shardDisconnect: [err: Error | undefined, id: number]; @@ -698,6 +719,7 @@ declare namespace Eris { interface GuildAuditLog { entries: GuildAuditLogEntry[]; integrations: GuildIntegration[]; + threads: AnyThreadChannel[]; users: User[]; webhooks: Webhook[]; } @@ -869,6 +891,7 @@ declare namespace Eris { } interface AdvancedMessageContent { allowedMentions?: AllowedMentions; + components?: ActionRow[]; content?: string; /** @deprecated */ embed?: EmbedOptions; @@ -877,6 +900,7 @@ declare namespace Eris { messageReference?: MessageReferenceReply; /** @deprecated */ messageReferenceID?: string; + stickerIDs?: string[]; tts?: boolean; } interface AdvancedMessageContentEdit extends AdvancedMessageContent { @@ -1039,7 +1063,7 @@ declare namespace Eris { interface PartialRole { color?: number; hoist?: boolean; - id?: number; + id: string; mentionable?: boolean; name?: string; permissions?: number; @@ -1059,34 +1083,50 @@ declare namespace Eris { } // Thread - interface ArchivedThreads { - hasMore: boolean; - members: ThreadMember[]; - threads: T[]; - } interface CreateThreadOptions { autoArchiveDuration: AutoArchiveDuration; name: string; } - interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { - type: AnyThreadChannel["type"]; + interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { + invitable: T extends PrivateThreadChannel["type"] ? boolean : never; + type: T; } interface GetArchivedThreadsOptions { before?: Date; limit?: number; } + interface ListedChannelThreads extends ListedGuildThreads { + hasMore: boolean; + } + interface ListedGuildThreads { + members: ThreadMember[]; + threads: T[]; + } + interface PrivateThreadMetadata extends ThreadMetadata { + invitable: boolean; + } interface ThreadMetadata { archiveTimestamp: number; archived: boolean; autoArchiveDuration: AutoArchiveDuration; - locked?: boolean; + locked: boolean; } // Voice + interface JoinVoiceChannelOptions { + opusOnly?: boolean; + selfDeaf?: boolean; + selfMute?: boolean; + shared?: boolean; + } interface StageInstanceOptions { privacyLevel?: StageInstancePrivacyLevel; topic?: string; } + interface UncachedMemberVoiceState { + id: string; + voiceState: OldVoiceState; + } interface VoiceConnectData { channel_id: string; endpoint: string; @@ -1239,6 +1279,10 @@ declare namespace Eris { STAGE_INSTANCE_CREATE: 83; STAGE_INSTANCE_UPDATE: 84; STAGE_INSTANCE_DELETE: 85; + + THREAD_CREATE: 110; + THREAD_UPDATE: 111; + THREAD_DELETE: 112; }; ChannelTypes: { GUILD_TEXT: 0; @@ -1248,9 +1292,9 @@ declare namespace Eris { GUILD_CATEGORY: 4; GUILD_NEWS: 5; GUILD_STORE: 6; - GUILD_NEWS_THREAD: 10, - GUILD_PUBLIC_THREAD: 11, - GUILD_PRIVATE_THREAD: 12, + GUILD_NEWS_THREAD: 10; + GUILD_PUBLIC_THREAD: 11; + GUILD_PRIVATE_THREAD: 12; GUILD_STAGE: 13; }; GATEWAY_VERSION: 9; @@ -1379,11 +1423,15 @@ declare namespace Eris { /** @deprecated */ useSlashCommands: 2147483648n; voiceRequestToSpeak: 4294967296n; + manageThreads: 17179869184n; + createPublicThreads: 34359738368n; + createPrivateThreads: 68719476736n; useExternalStickers: 137438953472n; + sendMessagesInThreads: 274877906944n; allGuild: 2080899262n; - allText: 140392266833n; + allText: 535529258065n; allVoice: 4629464849n; - all: 146028888063n; + all: 541165879295n; }; REST_VERSION: 9; StickerFormats: { @@ -1799,7 +1847,7 @@ declare namespace Eris { editGuildVoiceState(guildID: string, options: VoiceStateOptions, userID?: string): Promise; editGuildWelcomeScreen(guildID: string, options: WelcomeScreenOptions): Promise; editGuildWidget(guildID: string, options: Widget): Promise; - editMessage(channelID: string, messageID: string, content: MessageContent): Promise; + editMessage(channelID: string, messageID: string, content: MessageContentEdit): Promise; /** @deprecated */ editNickname(guildID: string, nick: string, reason?: string): Promise; editRole(guildID: string, roleID: string, options: RoleOptions, reason?: string): Promise; // TODO not all options are available? @@ -1836,9 +1884,11 @@ declare namespace Eris { executeWebhook(webhookID: string, token: string, options: WebhookPayload & { wait: true }): Promise>; executeWebhook(webhookID: string, token: string, options: WebhookPayload): Promise; followChannel(channelID: string, webhookChannelID: string): Promise; - getActiveThreads(channelID: string): Promise; - getArchivedThreads(channelID: string, type: "private", options?: GetArchivedThreadsOptions): Promise>; - getArchivedThreads(channelID: string, type: "public", options?: GetArchivedThreadsOptions): Promise>; + getActiveGuildThreads(guildID: string): Promise; + /** @deprecated */ + getActiveThreads(channelID: string): Promise; + getArchivedThreads(channelID: string, type: "private", options?: GetArchivedThreadsOptions): Promise>; + getArchivedThreads(channelID: string, type: "public", options?: GetArchivedThreadsOptions): Promise>; getBotGateway(): Promise<{ session_start_limit: { max_concurrency: number; remaining: number; reset_after: number; total: number }; shards: number; url: string }>; getChannel(channelID: string): AnyChannel; getChannelInvites(channelID: string): Promise; @@ -1867,7 +1917,7 @@ declare namespace Eris { getGuildWidgetSettings(guildID: string): Promise; getInvite(inviteID: string, withCounts?: false): Promise>; getInvite(inviteID: string, withCounts: true): Promise>; - getJoinedPrivateArchivedThreads(channelID: string, options?: GetArchivedThreadsOptions): Promise>; + getJoinedPrivateArchivedThreads(channelID: string, options?: GetArchivedThreadsOptions): Promise>; getMessage(channelID: string, messageID: string): Promise; getMessageReaction(channelID: string, messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -1934,7 +1984,7 @@ declare namespace Eris { getWebhook(webhookID: string, token?: string): Promise; getWebhookMessage(webhookID: string, token: string, messageID: string): Promise>; joinThread(channelID: string, userID?: string): Promise; - joinVoiceChannel(channelID: string, options?: { opusOnly?: boolean; shared?: boolean }): Promise; + joinVoiceChannel(channelID: string, options?: JoinVoiceChannelOptions): Promise; kickGuildMember(guildID: string, userID: string, reason?: string): Promise; leaveGuild(guildID: string): Promise; leaveThread(channelID: string, userID?: string): Promise; @@ -2223,6 +2273,7 @@ declare namespace Eris { editWidget(options: Widget): Promise; fetchAllMembers(timeout?: number): Promise; fetchMembers(options?: FetchMembersOptions): Promise; + getActiveThreads(): Promise; getAuditLog(options?: GetGuildAuditLogOptions): Promise; /** @deprecated */ getAuditLogs(limit?: number, before?: string, actionType?: number, userID?: string): Promise; @@ -2474,11 +2525,11 @@ declare namespace Eris { editedTimestamp?: number; embeds: Embed[]; flags: number; - guildID: T extends GuildTextable ? string : undefined; + guildID: T extends GuildTextableWithThread ? string : undefined; id: string; interaction: MessageInteraction | null; jumpLink: string; - member: T extends GuildTextable ? Member : null; + member: T extends GuildTextableWithThread ? Member : null; mentionEveryone: boolean; mentions: User[]; messageReference: MessageReference | null; @@ -2487,13 +2538,13 @@ declare namespace Eris { reactions: { [s: string]: { count: number; me: boolean } }; referencedMessage?: Message | null; roleMentions: string[]; + stickerItems?: StickerItems[]; /** @deprecated */ stickers?: Sticker[]; - stickerItems?: StickerItems[]; timestamp: number; tts: boolean; type: number; - webhookID: T extends GuildTextable ? string | undefined : undefined; + webhookID: T extends GuildTextableWithThread ? string | undefined : undefined; constructor(data: BaseData, client: Client); addReaction(reaction: string): Promise; /** @deprecated */ @@ -2534,7 +2585,8 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2622,6 +2674,27 @@ declare namespace Eris { getPins(): Promise[]>; } + export class PrivateThreadChannel extends ThreadChannel { + threadMetadata: PrivateThreadMetadata; + type: 12; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } + + export class PublicThreadChannel extends ThreadChannel { + type: 10 | 11; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } + export class Relationship extends Base implements Omit { activities: Activity[] | null; clientStatus?: ClientStatus; @@ -2796,16 +2869,16 @@ declare namespace Eris { } export class StageInstance extends Base { - client: Client; channel: StageChannel | Uncached; + client: Client; discoverableDisabled: boolean; guild: Guild | Uncached; privacyLevel: StageInstancePrivacyLevel; topic: string; constructor(data: BaseData, client: Client); - update(data: BaseData): void; delete(): Promise; edit(options: StageInstanceOptions): Promise; + update(data: BaseData): void; } export class StoreChannel extends GuildChannel { @@ -2827,16 +2900,19 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; + createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions): Promise; createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; edit(options: Omit, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; - getActiveThreads(): Promise; - getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; - getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; + /** @deprecated */ + getActiveThreads(): Promise; + getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; + getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; getInvites(): Promise<(Invite<"withMetadata", TextChannel>)[]>; - getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions): Promise>; + getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions): Promise>; getMessage(messageID: string): Promise>; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -2858,23 +2934,27 @@ declare namespace Eris { unsendMessage(messageID: string): Promise; } - export class ThreadChannel extends GuildChannel { + type A = T; + type B = A; + + export class ThreadChannel extends GuildChannel implements ThreadTextable { lastMessageID: string; + lastPinTimestamp?: number; member?: ThreadMember; memberCount: number; members: Collection; messageCount: number; - messages: Collection; + messages: Collection>; ownerID: string; rateLimitPerUser: number; threadMetadata: ThreadMetadata; type: 10 | 11 | 12; constructor(data: BaseData, client: Client, messageLimit?: number); addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; - edit(options: Pick, reason?: string): Promise; + edit(options: Pick, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMembers(): Promise; getMessage(messageID: string): Promise>; @@ -2895,11 +2975,11 @@ declare namespace Eris { export class ThreadMember extends Base { client: Client; - threadID: string; joinTimestamp: number; + threadID: string; constructor(data: BaseData, client: Client); - update(data: BaseData): void; leave(): Promise; + update(data: BaseData): void; } export class UnavailableGuild extends Base { @@ -2944,7 +3024,7 @@ declare namespace Eris { voiceMembers: Collection; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; getInvites(): Promise<(Invite<"withMetadata", VoiceChannel>)[]>; - join(options: { opusOnly?: boolean; shared?: boolean }): Promise; + join(options?: JoinVoiceChannelOptions): Promise; leave(): void; } diff --git a/lib/Client.js b/lib/Client.js index 016d5cbca7..c2cfc8d058 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -645,6 +645,7 @@ class Client extends EventEmitter { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object | Array} [file] A file object (or an Array of them) * @arg {Buffer} file.file A buffer containing file data @@ -659,13 +660,12 @@ class Client extends EventEmitter { }; } else if(content.content !== undefined && typeof content.content !== "string") { content.content = "" + content.content; - } else if(content.content === undefined && !content.embed && !content.embeds && !file) { - return Promise.reject(new Error("No content, file, or embeds")); } else if(content.embed && !content.embeds) { this.emit("warn", "[DEPRECATED] content.embed is deprecated. Use content.embeds instead"); content.embeds = [content.embed]; } content.allowed_mentions = this._formatAllowedMentions(content.allowedMentions); + content.sticker_ids = content.stickerIDs; if(content.messageReference) { content.message_reference = content.messageReference; if(content.messageReference.messageID !== undefined) { @@ -688,8 +688,6 @@ class Client extends EventEmitter { this.emit("warn", "[DEPRECATED] content.messageReferenceID is deprecated. Use content.messageReference instead"); content.message_reference = {message_id: content.messageReferenceID}; } - } else if(!file) { - return Promise.reject(new Error("No content, file, or embeds")); } return this.requestHandler.request("POST", Endpoints.CHANNEL_MESSAGES(channelID), true, content, file).then((message) => new Message(message, this)); } @@ -764,13 +762,15 @@ class Client extends EventEmitter { * @arg {String} channelID The ID of the channel * @arg {Object} options The thread options * @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 + * @arg {boolean} [options.invitable] Whether non-moderators can add other non-moderators to the thread (private threads only) * @arg {String} options.name The thread channel name - * @arg {Number} [options.type] The channel type of the thread to create. It is recommended by Discord to explicitly set this parameter as the default is highly likely to change in a future API version + * @arg {Number} [options.type] The channel type of the thread to create. It is recommended to explicitly set this property as this will be a required property in API v10 * @returns {Promise} */ createThreadWithoutMessage(channelID, options) { return this.requestHandler.request("POST", Endpoints.THREAD_WITHOUT_MESSAGE(channelID), true, { auto_archive_duration: options.autoArchiveDuration, + invitable: options.invitable, name: options.name, type: options.type }).then((channel) => Channel.from(channel, this)); @@ -1041,8 +1041,9 @@ class Client extends EventEmitter { * @arg {Boolean} [options.archived] The archive status of the channel (thread channels only) * @arg {Number} [options.autoArchiveDuration] The duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 (thread channels only) * @arg {Number} [options.bitrate] The bitrate of the channel (guild voice channels only) - * @arg {Number?} [options.defaultArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Number?} [options.defaultAutoArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) * @arg {String} [options.icon] The icon of the channel as a base64 data URI (group channels only). Note: base64 strings alone are not base64 data URI strings + * @arg {Boolean} [options.invitable] Whether non-moderators can add other non-moderators to the channel (private thread channels only) * @arg {Boolean} [options.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel * @arg {Boolean} [options.nsfw] The nsfw status of the channel (guild channels only) @@ -1061,8 +1062,9 @@ class Client extends EventEmitter { archived: options.archived, auto_archive_duration: options.autoArchiveDuration, bitrate: options.bitrate, - default_archive_duration: options.defaultArchiveDuration, + default_auto_archive_duration: options.defaultAutoArchiveDuration, icon: options.icon, + invitable: options.invitable, locked: options.locked, name: options.name, nsfw: options.nsfw, @@ -1386,8 +1388,6 @@ class Client extends EventEmitter { }; } else if(content.content !== undefined && typeof content.content !== "string") { content.content = "" + content.content; - } else if(content.content === undefined && !content.embed && !content.embeds && !content.components && content.flags === undefined) { - return Promise.reject(new Error("No content, embeds, components or flags")); } else if(content.embed && !content.embeds) { this.emit("warn", "[DEPRECATED] content.embed is deprecated. Use content.embeds instead"); content.embeds = [content.embed]; @@ -1666,9 +1666,6 @@ class Client extends EventEmitter { * @returns {Promise} */ editWebhookMessage(webhookID, token, messageID, options) { - if(!options.content && !options.embeds && !options.components && !options.file) { - return Promise.reject(new Error("No content, embed, components, or file")); - } if(options.allowedMentions) { options.allowed_mentions = this._formatAllowedMentions(options.allowedMentions); } @@ -1752,16 +1749,13 @@ class Client extends EventEmitter { * @arg {Object | Array} [options.file] A file object (or an Array of them) * @arg {Buffer} options.file.file A buffer containing file data * @arg {String} options.file.name What to name the file - * @arg {Number} [options.flags] Flags to execute the webhook with, 64 for ephemeral (Interaction webhooks only) + * @arg {String} [options.threadID] The ID of the thread channel in the webhook's channel to send the message to * @arg {Boolean} [options.tts=false] Whether the message should be a TTS message or not * @arg {String} [options.username] A custom username, defaults to webhook default username if not specified * @arg {Boolean} [options.wait=false] Whether to wait for the server to confirm the message create or not * @returns {Promise} */ executeWebhook(webhookID, token, options) { - if(!options.content && !options.file && !options.embeds) { - return Promise.reject(new Error("No content, embeds, or file")); - } let qs = ""; if(options.wait) { qs += "&wait=true"; @@ -1792,12 +1786,32 @@ class Client extends EventEmitter { } /** - * Get all active threads in a channel + * Get all active threads in a guild + * @arg {String} guildID The ID of the guild + * @returns {Promise} An object containing an array of `threads` and an array of `members` + */ + getActiveGuildThreads(guildID) { + return this.requestHandler.request("GET", Endpoints.THREADS_GUILD_ACTIVE(guildID), true).then((response) => { + return { + members: response.members.map((member) => new ThreadMember(member, this)), + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); + } + + /** + * [DEPRECATED] Get all active threads in a channel. Use getActiveGuildThreads instead * @arg {String} channelID The ID of the channel - * @returns {Promise>} + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getActiveThreads(channelID) { - return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((channels) => channels.map((c) => Channel.from(c, this))); + return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((response) => { + return { + hasMore: response.has_more, + members: response.members.map((member) => new ThreadMember(member, this)), + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); } /** @@ -1807,7 +1821,7 @@ class Client extends EventEmitter { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getArchivedThreads(channelID, type, options = {}) { return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED(channelID, type), true, options).then((response) => { @@ -1915,7 +1929,7 @@ class Client extends EventEmitter { * @arg {String} [options.before] Get entries before this entry ID * @arg {Number} [options.limit=50] The maximum number of entries to return * @arg {String} [options.userID] Filter entries by the user that performed the action - * @returns {Promise<{users: User[], entries: GuildAuditLogEntry[], integrations: PartialIntegration[], webhooks: Webhook[]}>} + * @returns {Promise<{entries: GuildAuditLogEntry[], integrations: PartialIntegration[], threads: (NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[], users: User[], webhooks: Webhook[]}>} */ getGuildAuditLog(guildID, options = {}, before, actionType, userID) { if(!options || typeof options !== "object") { @@ -1944,9 +1958,10 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.GUILD_AUDIT_LOGS(guildID), true, options).then((data) => { const guild = this.guilds.get(guildID); return { - users: data.users.map((user) => this.users.add(user, this)), entries: data.audit_log_entries.map((entry) => new GuildAuditLogEntry(entry, guild)), integrations: data.integrations.map((integration) => new GuildIntegration(integration, guild)), + threads: data.threads.map((thread) => guild.threads.update(thread, this)), + users: data.users.map((user) => this.users.add(user, this)), webhooks: data.webhooks }; }); @@ -2110,7 +2125,7 @@ class Client extends EventEmitter { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getJoinedPrivateArchivedThreads(channelID, options = {}) { return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { @@ -2547,6 +2562,8 @@ class Client extends EventEmitter { * @arg {Object} [options] VoiceConnection constructor options * @arg {Object} [options.opusOnly] Skip opus encoder initialization. You should not enable this unless you know what you are doing * @arg {Object} [options.shared] Whether the VoiceConnection will be part of a SharedStream or not + * @arg {Boolean} [options.selfMute] Whether the bot joins the channel muted or not + * @arg {Boolean} [options.selfDeaf] Whether the bot joins the channel deafened or not * @returns {Promise} Resolves with a VoiceConnection */ joinVoiceChannel(channelID, options = {}) { @@ -2560,8 +2577,8 @@ class Client extends EventEmitter { this.shards.get(this.guildShardMap[this.channelGuildMap[channelID]] || 0).sendWS(Constants.GatewayOPCodes.VOICE_STATE_UPDATE, { guild_id: this.channelGuildMap[channelID] || null, channel_id: channelID || null, - self_mute: false, - self_deaf: false + self_mute: options.selfMute || false, + self_deaf: options.selfDeaf || false }); if(options.opusOnly === undefined) { options.opusOnly = this.options.opusOnly; diff --git a/lib/Constants.js b/lib/Constants.js index 77f4409bc2..f6ecd5256c 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -67,7 +67,12 @@ const Permissions = { manageEmojisAndStickers: 1n << 30n, manageEmojis: 1n << 30n, // [DEPRECATED] useApplicationCommands: 1n << 31n, useSlashCommands: 1n << 31n, // [DEPRECATED] voiceRequestToSpeak: 1n << 32n, - useExternalStickers: 1n << 37n + + manageThreads: 1n << 34n, + createPublicThreads: 1n << 35n, + createPrivateThreads: 1n << 36n, + useExternalStickers: 1n << 37n, + sendMessagesInThreads: 1n << 38n }; Permissions.allGuild = Permissions.kickMembers | Permissions.banMembers @@ -96,7 +101,11 @@ Permissions.allText = Permissions.createInstantInvite | Permissions.manageRoles | Permissions.manageWebhooks | Permissions.useApplicationCommands - | Permissions.useExternalStickers; + | Permissions.manageThreads + | Permissions.createPublicThreads + | Permissions.createPrivateThreads + | Permissions.useExternalStickers + | Permissions.sendMessagesInThreads; Permissions.allVoice = Permissions.createInstantInvite | Permissions.manageChannels | Permissions.voicePrioritySpeaker @@ -189,7 +198,11 @@ module.exports.AuditLogActions = { INTEGRATION_DELETE: 82, STAGE_INSTANCE_CREATE: 83, STAGE_INSTANCE_UPDATE: 84, - STAGE_INSTANCE_DELETE: 85 + STAGE_INSTANCE_DELETE: 85, + + THREAD_CREATE: 110, + THREAD_UPDATE: 111, + THREAD_DELETE: 112 }; module.exports.MessageActivityTypes = { diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 4611161d08..5fcb5aa587 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2109,6 +2109,9 @@ class Shard extends EventEmitter { case "THREAD_UPDATE": { const channel = this.client.getChannel(packet.d.id); if(!channel) { + const thread = Channel.from(packet.d, this.client); + this.emit("threadUpdate", this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client), null); + this.client.threadGuildMap[packet.d.id] = packet.d.guild_id; break; } if(!(channel instanceof ThreadChannel)) { @@ -2126,7 +2129,7 @@ class Shard extends EventEmitter { * Fired when a thread channel is updated * @event Client#threadUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel - * @prop {Object} oldChannel + * @prop {Object?} oldChannel The old thread channel. This will be null if the channel was uncached * @prop {String} oldChannel.name The name of the channel * @prop {Number} oldChannel.rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled * @prop {Object} oldChannel.threadMetadata Metadata for the thread @@ -2218,7 +2221,22 @@ class Shard extends EventEmitter { removedMembers = packet.d.removed_member_ids.map((id) => channel.members.remove({id}) || {id}); } if(packet.d.added_members) { - addedMembers = packet.d.added_members.map((m) => channel.members.update(m, this.client)); + addedMembers = packet.d.added_members.map((m) => { + if(m.presence) { + m.presence.id = m.presence.user.id; + this.client.users.update(m.presence.user, this.client); + } + + m.thread_id = m.id; + m.id = m.user_id; + m.member.id = m.member.user.id; + const guild = this.client.guilds.get(packet.d.guild_id); + if(guild) { + guild.members.update(m.presence, guild); + guild.members.update(m.member); + } + return channel.members.update(m, this.client); + }); } /** * Fired when anyone is added or removed from a thread. If the `guildMembers` intent is not specified, this will only apply for the current user diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index b29aaa04ab..a4d3b6d615 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -80,6 +80,7 @@ module.exports.THREAD_WITHOUT_MESSAGE = (channelID) module.exports.THREADS_ACTIVE = (channelID) => `/channels/${channelID}/threads/active`; module.exports.THREADS_ARCHIVED = (channelID, type) => `/channels/${channelID}/threads/archived/${type}`; module.exports.THREADS_ARCHIVED_JOINED = (channelID) => `/channels/${channelID}/users/@me/threads/archived/private`; +module.exports.THREADS_GUILD_ACTIVE = (guildID) => `/guilds/${guildID}/threads/active`; module.exports.USER = (userID) => `/users/${userID}`; module.exports.USER_BILLING = (userID) => `/users/${userID}/billing`; module.exports.USER_BILLING_PAYMENTS = (userID) => `/users/${userID}/billing/payments`; diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 25bf5d88a1..3a4f9061fe 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -697,6 +697,14 @@ class Guild extends Base { return this.shard.requestGuildMembers(this.id, options); } + /** + * Get all active threads in this guild + * @returns {Promise} An object containing an array of `threads` and an array of `members` + */ + getActiveThreads() { + return this.client.getActiveThreads.call(this.client, this.id); + } + /** * Get the audit log for the guild * @arg {Object} [options] Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit` diff --git a/lib/structures/GuildAuditLogEntry.js b/lib/structures/GuildAuditLogEntry.js index 4ca2ac524b..9dace55707 100644 --- a/lib/structures/GuildAuditLogEntry.js +++ b/lib/structures/GuildAuditLogEntry.js @@ -11,7 +11,7 @@ const {AuditLogActions} = require("../Constants"); * For example, if a channel was renamed from #general to #potato, this would be `{name: "potato"}`` * @prop {Object?} before The properties of the targeted object before the action was taken * For example, if a channel was renamed from #general to #potato, this would be `{name: "general"}`` -* @prop {(CategoryChannel | TextChannel | VoiceChannel | StageChannel)?} channel The channel targeted in the entry, action types 26 (MEMBER_MOVE), 72/74/75 (MESSAGE_DELETE/PIN/UNPIN), and 83-85 (STAGE\_INSTANCE\_CREATE/UPDATE/DELETE) only +* @prop {(CategoryChannel | TextChannel | VoiceChannel | NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel | StageChannel)?} channel The channel targeted in the entry, action types 26 (MEMBER_MOVE), 72/74/75 (MESSAGE_DELETE/PIN/UNPIN) and 83/84/85 (STAGE_INSTANCE_CREATE/UPDATE/DELETE) only * @prop {Number?} count The number of entities targeted * For example, for action type 26 (MEMBER_MOVE), this is the number of members that were moved/disconnected from the voice channel * @prop {Number?} deleteMemberDays The number of days of inactivity to prune for, action type 21 (MEMBER_PRUNE) only @@ -123,7 +123,9 @@ class GuildAuditLogEntry extends Base { return this.guild && this.guild.emojis.find((emoji) => emoji.id === this.targetID); } else if(this.actionType < 80) { // Message return this.guild && this.guild.shard.client.users.get(this.targetID); - } else if(this.actionType < 90) { // Integrations/Stage instances + } else if(this.actionType < 83) { // Integrations + return null; + } else if(this.actionType < 90) { // Stage Instances return this.guild && this.guild.threads.get(this.targetID); } else { throw new Error("Unrecognized action type: " + this.actionType); diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 51acb7ad92..b0f3052d1b 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -13,7 +13,7 @@ const PermissionOverwrite = require("./PermissionOverwrite"); * @prop {String} id The ID of the channel * @prop {String} name The name of the channel * @prop {Boolean} nsfw Whether the channel is an NSFW channel or not -* @prop {String?} parentID The ID of the category this channel belongs to or the message ID where the thread originated from (thread channels only) +* @prop {String?} parentID The ID of the category this channel belongs to or the channel ID where the thread originated from (thread channels only) * @prop {Collection} permissionOverwrites Collection of PermissionOverwrites in this channel * @prop {Number} position The position of the channel */ @@ -73,7 +73,8 @@ class GuildChannel extends Channel { * @arg {Object} options The properties to edit * @arg {Boolean} [options.archived] The archive status of the channel (thread channels only) * @arg {Number} [options.autoArchiveDuration] The duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 (thread channels only) - * @arg {Number?} [options.defaultArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Number?} [options.defaultAutoArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Boolean} [options.invitable] Whether non-moderators can add other non-moderators to the channel (private thread channels only) * @arg {Boolean} [options.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel * @arg {String} [options.topic] The topic of the channel (guild text channels only) @@ -83,7 +84,7 @@ class GuildChannel extends Channel { * @arg {Number} [options.rateLimitPerUser] The time in seconds a user has to wait before sending another message (does not affect bots or users with manageMessages/manageChannel permissions) (guild text and thread channels only) * @arg {String?} [options.rtcRegion] The RTC region ID of the channel (automatic if `null`) (guild voice channels only) * @arg {Boolean} [options.nsfw] The nsfw status of the channel - * @arg {Number?} [options.parentID] The ID of the parent channel category for this channel (guild text/voice channels only) or the message ID where the thread originated from (thread channels only) + * @arg {Number?} [options.parentID] The ID of the parent channel category for this channel (guild text/voice channels only) or the channel ID where the thread originated from (thread channels only) * @arg {String} [reason] The reason to be displayed in audit logs * @returns {Promise} */ @@ -127,20 +128,21 @@ class GuildChannel extends Channel { if(permission & Permissions.administrator) { return new Permission(Permissions.all); } - let overwrite = this.permissionOverwrites.get(this.guild.id); + const channel = this instanceof ThreadChannel ? this.guild.channels.get(this.parentID) : this; + let overwrite = channel && channel.permissionOverwrites.get(this.guild.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } let deny = 0n; let allow = 0n; for(const roleID of member.roles) { - if((overwrite = this.permissionOverwrites.get(roleID))) { + if((overwrite = channel && channel.permissionOverwrites.get(roleID))) { deny |= overwrite.deny; allow |= overwrite.allow; } } permission = (permission & ~deny) | allow; - overwrite = this.permissionOverwrites.get(member.id); + overwrite = channel && channel.permissionOverwrites.get(member.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } @@ -160,3 +162,5 @@ class GuildChannel extends Channel { } module.exports = GuildChannel; + +const ThreadChannel = require("./ThreadChannel"); diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js index cc6d7dbfb7..db3364bb7e 100644 --- a/lib/structures/Invite.js +++ b/lib/structures/Invite.js @@ -55,7 +55,7 @@ class Invite extends Base { return m; }); this.stageInstance = { - members: this.guild.members.update(data.stage_instance.members, this.guild), + members: data.stage_instance.members.map((m) => this.guild.members.update(m, this.guild)), participantCount: data.stage_instance.participant_count, speakerCount: data.stage_instance.speaker_count, topic: data.stage_instance.topic diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 886122fa41..b427d91a90 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -434,23 +434,23 @@ class Message extends Base { /** * Edit the message * @arg {String | Array | Object} content A string, array of strings, or object. If an object is passed: - * @arg {Array} [options.components] An array of component objects - * @arg {String} [options.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only) - * @arg {Boolean} [options.components[].disabled] Whether the component is disabled (type 2 only) - * @arg {Object} [options.components[].emoji] The emoji to be displayed in the component (type 2) - * @arg {String} [options.components[].label] The label to be displayed in the component (type 2) + * @arg {Array} [content.components] An array of component objects + * @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only) + * @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 only) + * @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2) + * @arg {String} [content.components[].label] The label to be displayed in the component (type 2) * @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1) * @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1) - * @arg {Array} [options.components[].options] The options for this component (type 3 only) - * @arg {Boolean} [options.components[].options[].default] Whether this option should be the default value selected - * @arg {String} [options.components[].options[].description] The description for this option - * @arg {Object} [options.components[].options[].emoji] The emoji to be displayed in this option - * @arg {String} options.components[].options[].label The label for this option - * @arg {Number | String} options.components[].options[].value The value for this option - * @arg {String} [options.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only) - * @arg {Number} [options.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required - * @arg {Number} options.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu - * @arg {String} [options.components[].url] The URL that the component should open for users (type 2 style 5 only) + * @arg {Array} [content.components[].options] The options for this component (type 3 only) + * @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected + * @arg {String} [content.components[].options[].description] The description for this option + * @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option + * @arg {String} content.components[].options[].label The label for this option + * @arg {Number | String} content.components[].options[].value The value for this option + * @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only) + * @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required + * @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu + * @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only) * @arg {String} content.content A content string * @arg {Boolean} [content.disableEveryone] Whether to filter @everyone/@here or not (overrides default) * @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure diff --git a/lib/structures/PrivateChannel.js b/lib/structures/PrivateChannel.js index a4eca21e72..8ae63e89d2 100644 --- a/lib/structures/PrivateChannel.js +++ b/lib/structures/PrivateChannel.js @@ -71,6 +71,7 @@ class PrivateChannel extends Channel { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to the stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object} [file] A file object * @arg {Buffer} file.file A buffer containing file data diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js index f865f12503..69643e6d26 100644 --- a/lib/structures/PrivateThreadChannel.js +++ b/lib/structures/PrivateThreadChannel.js @@ -5,10 +5,29 @@ const ThreadChannel = require("./ThreadChannel"); /** * Represents a private thread channel. See ThreadChannel for extra properties. * @extends ThreadChannel +* @prop {Object} threadMetadata Metadata for the thread +* @prop {Number} threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity +* @prop {Boolean} threadMetadata.archived Whether the thread is archived +* @prop {Number} threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 +* @prop {Boolean} threadMetadata.invitable Whether non-moderators can add other non-moderators to the thread +* @prop {Boolean} threadMetadata.locked Whether the thread is locked */ class PrivateThreadChannel extends ThreadChannel { constructor(data, client, messageLimit) { super(data, client, messageLimit); + this.update(data); + } + + update(data) { + if(data.thread_metadata !== undefined) { + this.threadMetadata = { + archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp), + archived: data.thread_metadata.archived, + autoArchiveDuration: data.thread_metadata.auto_archive_duration, + invitable: data.thread_metadata.invitable, + locked: data.thread_metadata.locked + }; + } } } diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 3ac027b9bc..d34a339ab0 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -96,6 +96,7 @@ class TextChannel extends GuildChannel { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to the stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object} [file] A file object * @arg {Buffer} file.file A buffer containing file data @@ -122,8 +123,9 @@ class TextChannel extends GuildChannel { * Create a thread without an existing message * @arg {Object} options The thread options * @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 + * @arg {boolean} [options.invitable] Whether non-moderators can add other non-moderators to the thread (private threads only) * @arg {String} options.name The thread channel name - * @arg {Number} [options.type] The channel type of the thread to create. It is recommended by Discord to explicitly set this parameter as the default is highly likely to change in a future API version + * @arg {Number} [options.type] The channel type of the thread to create. It is recommended to explicitly set this property as this will be a required property in API v10 * @returns {Promise} */ createThreadWithoutMessage(options) { @@ -202,8 +204,8 @@ class TextChannel extends GuildChannel { } /** - * Get all active threads in this channel - * @returns {Promise>} + * [DEPRECATED] Get all active threads in this channel. Use guild.getActiveThreads instead + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getActiveThreads() { return this.client.getActiveThreads.call(this.client, this.id); @@ -215,7 +217,7 @@ class TextChannel extends GuildChannel { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getArchivedThreads(type, options) { return this.client.getArchivedThreads.call(this.client, this.id, type, options); @@ -234,7 +236,7 @@ class TextChannel extends GuildChannel { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getJoinedPrivateArchivedThreads(options) { return this.client.getJoinedPrivateArchivedThreads.call(this.client, this.id, options); diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 2b3e4c8833..75c32e2129 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -24,7 +24,7 @@ const ThreadMember = require("./ThreadMember"); * @prop {Number} threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity * @prop {Boolean} threadMetadata.archived Whether the thread is archived * @prop {Number} threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 -* @prop {Boolean?} threadMetadata.locked Whether the thread is locked +* @prop {Boolean} threadMetadata.locked Whether the thread is locked */ class ThreadChannel extends GuildChannel { constructor(data, client, messageLimit) { @@ -51,7 +51,6 @@ class ThreadChannel extends GuildChannel { this.threadMetadata = { archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp), archived: data.thread_metadata.archived, - archiverID: data.thread_metadata.archiver_id, autoArchiveDuration: data.thread_metadata.auto_archive_duration, locked: data.thread_metadata.locked }; diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js index 8a2c17655e..91dbb44c5b 100644 --- a/lib/structures/ThreadMember.js +++ b/lib/structures/ThreadMember.js @@ -8,6 +8,8 @@ class ThreadMember extends Base { this.client = client; this.threadID = data.thread_id || data.id; // Thanks Discord this.joinTimestamp = Date.parse(data.join_timestamp); + // this.guildMember FIXME We need to somehow get the guild for this to be possible. Ping me in the Eris server if you have suggestions or if we should just leave this out + this.update(data); } update(data) { diff --git a/lib/structures/VoiceChannel.js b/lib/structures/VoiceChannel.js index 1e68c370a2..c9615eaf7a 100644 --- a/lib/structures/VoiceChannel.js +++ b/lib/structures/VoiceChannel.js @@ -65,6 +65,8 @@ class VoiceChannel extends GuildChannel { * @arg {Object} [options] VoiceConnection constructor options * @arg {Object} [options.opusOnly] Skip opus encoder initialization. You should not enable this unless you know what you are doing * @arg {Object} [options.shared] Whether the VoiceConnection will be part of a SharedStream or not + * @arg {Boolean} [options.selfMute] Whether the bot joins the channel muted or not + * @arg {Boolean} [options.selfDeaf] Whether the bot joins the channel deafened or not * @returns {Promise} Resolves with a VoiceConnection */ join(options) {