From 90076d70ddf404e779fcf30a815e60817a1326b8 Mon Sep 17 00:00:00 2001 From: Bsian Date: Mon, 24 May 2021 22:10:26 +0100 Subject: [PATCH 01/90] v9 Constants --- index.d.ts | 18 +++++++++++++----- lib/Constants.js | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/index.d.ts b/index.d.ts index 31f649c9e..e554adf47 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1147,9 +1147,12 @@ 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_STAGE: 13; }; - GATEWAY_VERSION: 8; + GATEWAY_VERSION: 9; GatewayOPCodes: { EVENT: 0; HEARTBEAT: 1; @@ -1200,6 +1203,7 @@ declare namespace Eris { SUPPRESS_EMBEDS: 4; SOURCE_MESSAGE_DELETED: 8; URGENT: 16; + HAS_THREAD: 32; }; MessageTypes: { DEFAULT: 0; @@ -1220,9 +1224,10 @@ declare namespace Eris { GUILD_DISCOVERY_REQUALIFIED: 15; GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING: 16; GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING: 17; + THREAD_CREATED: 18; REPLY: 19; APPLICATION_COMMAND: 20; - + THREAD_STARTER_MESSAGE: 21; GUILD_INVITE_REMINDER: 22; }; Permissions: { @@ -1267,12 +1272,15 @@ declare namespace Eris { manageEmojis: 1073741824n; useSlashCommands: 2147483648n; voiceRequestToSpeak: 4294967296n; + manageThreads: 17179869184n; + usePublicThreads: 34359738368n; + usePrivateThreads: 68719476736n; allGuild: 2080899262n; - allText: 2953313361n; + allText: 123212397649n; allVoice: 4629464849n; - all: 8589934591n; + all: 128849018879n; }; - REST_VERSION: 8; + REST_VERSION: 9; StickerFormats: { PNG: 1; APNG: 2; diff --git a/lib/Constants.js b/lib/Constants.js index f482e1504..8ce0fd2be 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -30,8 +30,8 @@ module.exports.GatewayOPCodes = { SYNC_CALL: 13 }; -module.exports.GATEWAY_VERSION = 8; -module.exports.REST_VERSION = 8; +module.exports.GATEWAY_VERSION = 9; +module.exports.REST_VERSION = 9; const Permissions = { createInstantInvite: 1n, @@ -66,7 +66,11 @@ const Permissions = { manageWebhooks: 1n << 29n, manageEmojis: 1n << 30n, useSlashCommands: 1n << 31n, - voiceRequestToSpeak: 1n << 32n + voiceRequestToSpeak: 1n << 32n, + + manageThreads: 1n << 34n, + usePublicThreads: 1n << 35n, + usePrivateThreads: 1n << 36n }; Permissions.allGuild = Permissions.kickMembers | Permissions.banMembers @@ -94,7 +98,10 @@ Permissions.allText = Permissions.createInstantInvite | Permissions.useExternalEmojis | Permissions.manageRoles | Permissions.manageWebhooks - | Permissions.useSlashCommands; + | Permissions.useSlashCommands + | Permissions.manageThreads + | Permissions.usePublicThreads + | Permissions.usePrivateThreads; Permissions.allVoice = Permissions.createInstantInvite | Permissions.manageChannels | Permissions.voicePrioritySpeaker @@ -199,7 +206,8 @@ module.exports.MessageFlags = { IS_CROSSPOST: 1 << 1, SUPPRESS_EMBEDS: 1 << 2, SOURCE_MESSAGE_DELETED: 1 << 3, - URGENT: 1 << 4 + URGENT: 1 << 4, + HAS_THREAD: 1 << 5 }; module.exports.MessageTypes = { @@ -221,21 +229,25 @@ module.exports.MessageTypes = { GUILD_DISCOVERY_REQUALIFIED: 15, GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING: 16, GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING: 17, + THREAD_CREATED: 18, REPLY: 19, APPLICATION_COMMAND: 20, - + THREAD_STARTER_MESSAGE: 21, GUILD_INVITE_REMINDER: 22 }; module.exports.ChannelTypes = { - GUILD_TEXT: 0, - DM: 1, - GUILD_VOICE: 2, - GROUP_DM: 3, - GUILD_CATEGORY: 4, - GUILD_NEWS: 5, - GUILD_STORE: 6, - GUILD_STAGE: 13 + GUILD_TEXT: 0, + DM: 1, + GUILD_VOICE: 2, + GROUP_DM: 3, + GUILD_CATEGORY: 4, + GUILD_NEWS: 5, + GUILD_STORE: 6, + GUILD_NEWS_THREAD: 10, + GUILD_PUBLIC_THREAD: 11, + GUILD_PRIVATE_THREAD: 12, + GUILD_STAGE: 13 }; module.exports.UserFlags = { From 21e23b660e2d4d546741374a1b355e68aa61ceca Mon Sep 17 00:00:00 2001 From: Bsian Date: Mon, 24 May 2021 23:15:19 +0100 Subject: [PATCH 02/90] v9 endpoints --- lib/rest/Endpoints.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index e974dfe8f..5a93e729a 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -58,6 +58,13 @@ module.exports.GUILD_ROLES = (guildID) module.exports.GUILD_TEMPLATE = (code) => `/guilds/templates/${code}`; module.exports.GUILD_TEMPLATES = (guildID) => `/guilds/${guildID}/templates`; module.exports.GUILD_TEMPLATE_GUILD = (guildID, code) => `/guilds/${guildID}/templates/${code}`; +module.exports.GUILD_THREAD_MEMBER = (channelID, userID) => `/channels/${channelID}/thread-members/${userID}`; +module.exports.GUILD_THREAD_MEMBERS = (channelID) => `/channels/${channelID}/thread-members`; +module.exports.GUILD_THREAD_PRIVATE = (channelID) => `/channels/${channelID}/threads`; +module.exports.GUILD_THREAD_PUBLIC = (channelID, msgID) => `/channels/${channelID}/messages/${msgID}/threads`; +module.exports.GUILD_THREADS_ACTIVE = (channelID) => `/channels/${channelID}/threads/active`; +module.exports.GUILD_THREADS_ARCHIVED = (channelID, type) => `/channels/${channelID}/threads/archived/${type}`; +module.exports.GUILD_THREADS_ARCHIVED_JOINED = (channelID) => `/channels/${channelID}/users/@me/threads/archived/private`; module.exports.GUILD_VOICE_REGIONS = (guildID) => `/guilds/${guildID}/regions`; module.exports.GUILD_WEBHOOKS = (guildID) => `/guilds/${guildID}/webhooks`; module.exports.GUILD_WELCOME_SCREEN = (guildID) => `/guilds/${guildID}/welcome-screen`; From 7403f8d47b6c40c410a5d3e882d6d84d7751e890 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 09:11:23 +0100 Subject: [PATCH 03/90] New classes and methods --- esm.mjs | 4 + index.d.ts | 40 ++++++++- index.js | 4 + lib/Client.js | 118 ++++++++++++++++++++++++- lib/structures/GuildChannel.js | 7 +- lib/structures/Message.js | 11 +++ lib/structures/NewsThreadChannel.js | 16 ++++ lib/structures/PrivateThreadChannel.js | 16 ++++ lib/structures/PublicThreadChannel.js | 16 ++++ lib/structures/TextChannel.js | 54 +++++++++++ lib/structures/ThreadChannel.js | 80 +++++++++++++++++ 11 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 lib/structures/NewsThreadChannel.js create mode 100644 lib/structures/PrivateThreadChannel.js create mode 100644 lib/structures/PublicThreadChannel.js create mode 100644 lib/structures/ThreadChannel.js diff --git a/esm.mjs b/esm.mjs index 810e5eb01..e6adea5d4 100644 --- a/esm.mjs +++ b/esm.mjs @@ -28,9 +28,12 @@ export const { Member, Message, NewsChannel, + NewsThreadChannel, Permission, PermissionOverwrite, PrivateChannel, + PrivateThreadChannel, + PublicThreadChannel, Relationship, RequestHandler, Role, @@ -39,6 +42,7 @@ export const { SharedStream, StoreChannel, TextChannel, + ThreadChannel, UnavailableGuild, User, VERSION, diff --git a/index.d.ts b/index.d.ts index e554adf47..de2286995 100644 --- a/index.d.ts +++ b/index.d.ts @@ -119,7 +119,10 @@ declare namespace Eris { userLimit?: number; } interface EditChannelOptions extends Omit { + archived?: boolean; + autoArchiveDuration?: number; icon?: string; + locked?: boolean; name?: string; ownerID?: string; rtcRegion?: string | null; @@ -1638,6 +1641,8 @@ declare namespace Eris { createGuildFromTemplate(code: string, name: string, icon?: string): Promise; createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createMessage(channelID: string, content: MessageContent, file?: MessageFile | MessageFile[]): Promise; + createPrivateThread(channelID: string, options: unknown): Promise; // TODO Create thread options + createPublicThread(channelID: string, options: unknown): Promise; // TODO Create thread options createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; @@ -1723,6 +1728,8 @@ 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<(NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[]>; // TODO Type alias + getArchivedThreads(channelID: string, type: "public" | "private", options?: unknown): Promise; // TODO Get archived thread options and return object 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; @@ -1751,6 +1758,7 @@ declare namespace Eris { getGuildWidgetSettings(guildID: string): Promise; getInvite(inviteID: string, withCounts?: false): Promise>; getInvite(inviteID: string, withCounts: true): Promise>; + getJoinedPrivateArchivedThreads(channelID: string, options?: unknown): Promise; // TODO Get archived thread options and return object getMessage(channelID: string, messageID: string): Promise; getMessageReaction(channelID: string, messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -1761,7 +1769,7 @@ declare namespace Eris { getOAuthApplication(appID?: string): Promise; getPins(channelID: string): Promise; getPruneCount(guildID: string, options?: GetPruneOptions): Promise; - getRESTChannel(channelID: string): Promise; + getRESTChannel(channelID: string): Promise; // TODO Type alias getRESTGuild(guildID: string, withCounts?: boolean): Promise; getRESTGuildChannels(guildID: string): Promise; getRESTGuildEmoji(guildID: string, emojiID: string): Promise; @@ -1810,13 +1818,16 @@ declare namespace Eris { status: number; }[]>; getSelfSettings(): Promise; + getThreadMembers(channelID: string): Promise; // TODO Thread member getUserProfile(userID: string): Promise; getVoiceRegions(guildID?: string): Promise; 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; kickGuildMember(guildID: string, userID: string, reason?: string): Promise; leaveGuild(guildID: string): Promise; + leaveThread(channelID: string, userID?: string): Promise; leaveVoiceChannel(channelID: string): void; pinMessage(channelID: string, messageID: string): Promise; pruneMembers(guildID: string, options?: PruneMemberOptions): Promise; @@ -2319,6 +2330,7 @@ declare namespace Eris { addReaction(reaction: string): Promise; /** @deprecated */ addReaction(reaction: string, userID: string): Promise; + createPublicThread(options: unknown): Promise; // TODO Create thread options crosspost(): Promise : never>; delete(reason?: string): Promise; deleteWebhook(token: string): Promise; @@ -2351,6 +2363,8 @@ declare namespace Eris { getPins(): Promise[]>; } + export class NewsThreadChannel extends ThreadChannel {} + export class Permission extends Base { allow: bigint; deny: bigint; @@ -2413,6 +2427,10 @@ declare namespace Eris { unpinMessage(messageID: string): Promise; unsendMessage(messageID: string): Promise; } + + export class PrivateThreadChannel extends ThreadChannel {} + + export class PublicThreadChannel extends ThreadChannel {} export class Relationship extends Base implements Omit { activities: Activity[] | null; @@ -2587,12 +2605,17 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + createPrivateThread(options: unknown): Promise; // TODO Create thread options + createPublicThread(messageID: string, options: unknown): Promise; // TODO Create thread options 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: MessageContent): Promise>; + getActiveThreads(): Promise<(NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[]>; // Type alias + getArchivedThreads(type: "public" | "private", options: unknown): Promise; // TODO Get archived thread options and return object getInvites(): Promise<(Invite<"withMetadata", TextChannel>)[]>; + getJoinedPrivateArchivedThreads(options: unknown): Promise; // TODO Get archived thread options and return object getMessage(messageID: string): Promise>; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -2614,6 +2637,21 @@ declare namespace Eris { unsendMessage(messageID: string): Promise; } + export class ThreadChannel extends GuildChannel { + lastMessageID: string; + memberCount: number; + messageCount: number; + messages: Collection; + ownerID: string; + rateLimitPerUser: number; + threadMetadata: unknown; // TODO Thread metadata + member?: unknown; // TODO Thread member + constructor(data: BaseData, client: Client, messageLimit?: number); + getMembers(): Promise; // TODO Thread member + join(userID?: string): Promise; + leave(userID?: string): Promise; + } + export class UnavailableGuild extends Base { createdAt: number; id: string; diff --git a/index.js b/index.js index fee8b34d7..dda1c198f 100644 --- a/index.js +++ b/index.js @@ -29,9 +29,12 @@ Eris.Invite = require("./lib/structures/Invite"); Eris.Member = require("./lib/structures/Member"); Eris.Message = require("./lib/structures/Message"); Eris.NewsChannel = require("./lib/structures/NewsChannel"); +Eris.NewsThreadChannel = require("./lib/structures/NewsThreadChannel"); Eris.Permission = require("./lib/structures/Permission"); Eris.PermissionOverwrite = require("./lib/structures/PermissionOverwrite"); Eris.PrivateChannel = require("./lib/structures/PrivateChannel"); +Eris.PrivateThreadChannel = require("./lib/structures/PrivateThreadChannel"); +Eris.PublicThreadChannel = require("./lib/structures/PublicThreadChannel"); Eris.Relationship = require("./lib/structures/Relationship"); Eris.RequestHandler = require("./lib/rest/RequestHandler"); Eris.Role = require("./lib/structures/Role"); @@ -40,6 +43,7 @@ Eris.Shard = require("./lib/gateway/Shard"); Eris.SharedStream = require("./lib/voice/SharedStream"); Eris.StoreChannel = require("./lib/structures/StoreChannel"); Eris.TextChannel = require("./lib/structures/TextChannel"); +Eris.ThreadChannel = require("./lib/structures/ThreadChannel"); Eris.UnavailableGuild = require("./lib/structures/UnavailableGuild"); Eris.User = require("./lib/structures/User"); Eris.VERSION = require("./package.json").version; diff --git a/lib/Client.js b/lib/Client.js index ca84ac412..838237593 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -637,6 +637,37 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.CHANNEL_MESSAGES(channelID), true, content, file).then((message) => new Message(message, this)); } + /** + * Create a private thread + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createPrivateThread(channelID, options) { + return this.requestHandler.request("POST", Endpoints.GUILD_THREAD_PRIVATE(channelID), true, { + name: options.name, + auto_archive_duration: options.autoArchiveDuration + }).then((channel) => Channel.from(channel, this)); + } + + /** + * Create a public thread + * @arg {String} channelID The ID of the channel + * @arg {String} messageID The ID of the message to create the thread from + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createPublicThread(channelID, messageID, options) { + return this.requestHandler.request("POST", Endpoints.GUILD_THREAD_PUBLIC(channelID, messageID), true, { + name: options.name, + auto_archive_duration: options.autoArchiveDuration + }).then((channel) => Channel.from(channel, this)); + } + /** * Create a guild role * @arg {String} guildID The ID of the guild to create the role in @@ -923,24 +954,30 @@ class Client extends EventEmitter { * Edit a channel's properties * @arg {String} channelID The ID of the 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.bitrate] The bitrate of the channel (guild voice 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.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) * @arg {String} [options.ownerID] The ID of the channel owner (group channels only) * @arg {String?} [options.parentID] The ID of the parent channel category for this channel (guild text/voice channels only) - * @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 channels only) + * @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 {String} [options.topic] The topic of the channel (guild text channels only) * @arg {Number} [options.userLimit] The channel user limit (guild voice channels only) * @arg {Number} [options.videoQualityMode] The camera video quality mode of the channel (guild voice channels only). `1` is auto, `2` is 720p * @arg {String} [reason] The reason to be displayed in audit logs - * @returns {Promise} + * @returns {Promise} */ editChannel(channelID, options, reason) { return this.requestHandler.request("PATCH", Endpoints.CHANNEL(channelID), true, { + archived: options.archived, + auto_archive_duration: options.autoArchiveDuration, bitrate: options.bitrate, icon: options.icon, + locked: options.locked, name: options.name, nsfw: options.nsfw, owner_id: options.ownerID, @@ -1584,6 +1621,34 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.CHANNEL_FOLLOW(channelID), true, {webhook_channel_id: webhookChannelID}); } + /** + * Get all active threads in a channel + * @arg {String} channelID The ID of the channel + * @returns {Promise>} + */ + getActiveThreads(channelID) { + return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ACTIVE(channelID), true).then((channels) => channels.map((c) => Channel.from(c, this))); + } + + /** + * Get all archived threads in a channel + * @arg {String} channelID The ID of the channel + * @arg {String} type The type of thread channel, either "public" or "private" + * @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 + */ + getArchivedThreads(channelID, type, options = {}) { + return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED(channelID, type), true, options).then((response) => { + return { + hasMore: response.has_more, + members: response.members, // TODO Class ThreadMember + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); + } + /** * Get general and bot-specific info on connecting to the Discord gateway (e.g. connection ratelimit) * @returns {Promise} Resolves with an object containing gateway connection info @@ -1866,6 +1931,24 @@ class Client extends EventEmitter { }).then((invite) => new Invite(invite, this)); } + /** + * Get joined private archived threads in a channel + * @arg {String} channelID The ID of the channel + * @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 + */ + getJoinedPrivateArchivedThreads(channelID, options = {}) { + return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { + return { + hasMore: response.has_more, + members: response.members, // TODO Class ThreadMember + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); + } + /** * Get a previous message in a channel * @arg {String} channelID The ID of the channel @@ -2012,7 +2095,7 @@ class Client extends EventEmitter { /** * Get a channel's data via the REST API. REST mode is required to use this endpoint. * @arg {String} channelID The ID of the channel - * @returns {Promise} + * @returns {Promise} */ getRESTChannel(channelID) { if(!this.options.restMode) { @@ -2218,6 +2301,15 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.USER_SETTINGS("@me"), true); } + /** + * Get a list of members that are part of a thread channel + * @arg {String} channelID The ID of the thread channel + * @returns {Promise>} // TODO Class ThreadMember + */ + getThreadMembers(channelID) { + return this.requestHandler.request("GET", Endpoints.GUILD_THREAD_MEMBERS(channelID), true); + } + /** * [USER ACCOUNT] Get profile data for a user * @arg {String} userID The ID of the target user @@ -2257,6 +2349,16 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.WEBHOOK_MESSAGE(webhookID, token, messageID)).then((message) => new Message(message, this)); } + /** + * Join a thread + * @arg {String} channelID The ID of the thread channel + * @arg {String} [userID="@me"] The user ID of the user joining + * @returns {Promise} + */ + joinThread(channelID, userID = "@me") { + return this.requestHandler.request("PUT", Endpoints.GUILD_THREAD_MEMBER(channelID, userID), true); + } + /** * Join a voice channel. If joining a group call, the voice connection ID will be stored in voiceConnections as "call". Otherwise, it will be the guild ID * @arg {String} channelID The ID of the voice channel @@ -2307,6 +2409,16 @@ class Client extends EventEmitter { return this.requestHandler.request("DELETE", Endpoints.USER_GUILD("@me", guildID), true); } + /** + * Leave a thread + * @arg {String} channelID The ID of the thread channel + * @arg {String} [userID="@me"] The user ID of the user leaving + * @returns {Promise} + */ + leaveThread(channelID, userID = "@me") { + return this.requestHandler.request("DELETE", Endpoints.GUILD_THREAD_MEMBER(channelID, userID), true); + } + /** * Leaves a voice channel * @arg {String} channelID The ID of the voice channel diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index dded7552e..b20082f57 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -71,17 +71,20 @@ class GuildChannel extends Channel { /** * Edit the channel's properties * @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 {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) * @arg {Number} [options.bitrate] The bitrate of the channel (guild voice channels only) * @arg {Number} [options.userLimit] The channel user limit (guild voice channels only) * @arg {Number} [options.videoQualityMode] The camera video quality mode of the channel (guild voice channels only). `1` is auto, `2` is 720p - * @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 channels only) + * @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) * @arg {String} [reason] The reason to be displayed in audit logs - * @returns {Promise} + * @returns {Promise} */ edit(options, reason) { return this.client.editChannel.call(this.client, this.id, options, reason); diff --git a/lib/structures/Message.js b/lib/structures/Message.js index f7ed66720..f3e050fe2 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -372,6 +372,17 @@ class Message extends Base { return this._client.addMessageReaction.call(this._client, this.channel.id, this.id, reaction, userID); } + /** + * Create a public thread from this 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 {String} options.name The thread channel name + * @returns {Promise} + */ + createPublicThread(options) { + return this.client.createPublicThread.call(this.client, this.channel.id, this.id, options); + } + /** * Crosspost (publish) a message to subscribed channels (NewsChannel only) * @returns {Promise} diff --git a/lib/structures/NewsThreadChannel.js b/lib/structures/NewsThreadChannel.js new file mode 100644 index 000000000..8cce63878 --- /dev/null +++ b/lib/structures/NewsThreadChannel.js @@ -0,0 +1,16 @@ +"use strict"; + +const ThreadChannel = require("./ThreadChannel"); + +/** +* Represents a news thread channel. See ThreadChannel for extra properties. +* @extends ThreadChannel +*/ +class NewsThreadChannel extends ThreadChannel { + constructor(data, guild, messageLimit) { + super(data, guild, messageLimit); + this.update(data); + } +} + +module.exports = NewsThreadChannel; diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js new file mode 100644 index 000000000..0ffa1d13a --- /dev/null +++ b/lib/structures/PrivateThreadChannel.js @@ -0,0 +1,16 @@ +"use strict"; + +const ThreadChannel = require("./ThreadChannel"); + +/** +* Represents a private thread channel. See ThreadChannel for extra properties. +* @extends ThreadChannel +*/ +class PrivateThreadChannel extends ThreadChannel { + constructor(data, guild, messageLimit) { + super(data, guild, messageLimit); + this.update(data); + } +} + +module.exports = PrivateThreadChannel; diff --git a/lib/structures/PublicThreadChannel.js b/lib/structures/PublicThreadChannel.js new file mode 100644 index 000000000..ecbebbe54 --- /dev/null +++ b/lib/structures/PublicThreadChannel.js @@ -0,0 +1,16 @@ +"use strict"; + +const ThreadChannel = require("./ThreadChannel"); + +/** +* Represents a public thread channel. See ThreadChannel for extra properties. +* @extends ThreadChannel +*/ +class PublicThreadChannel extends ThreadChannel { + constructor(data, guild, messageLimit) { + super(data, guild, messageLimit); + this.update(data); + } +} + +module.exports = PublicThreadChannel; diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 9ff0373bd..a417c2256 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -86,6 +86,29 @@ class TextChannel extends GuildChannel { return this.client.createMessage.call(this.client, this.id, content, file); } + /** + * Create a private thread + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createPrivateThread(options) { + return this.client.createPrivateThread.call(this.client, this.id, options); + } + + /** + * Create a public thread + * @arg {String} messageID The ID of the message to create the thread from + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createPublicThread(messageID, options) { + return this.client.createPublicThread.call(this.client, this.id, messageID, options); + } + /** * Create a channel webhook * @arg {Object} options Webhook options @@ -137,6 +160,26 @@ class TextChannel extends GuildChannel { return this.client.editMessage.call(this.client, this.id, messageID, content); } + /** + * Get all active threads in this channel + * @returns {Promise>} + */ + getActiveThreads() { + return this.client.getActiveThreads.call(this.client, this.id); + } + + /** + * Get all archived threads in this channel + * @arg {String} type The type of thread channel, either "public" or "private" + * @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 + */ + getArchivedThreads(type, options) { + return this.client.getArchivedThreads.call(this.client, this.id, type, options); + } + /** * Get all invites in the channel * @returns {Promise>} @@ -145,6 +188,17 @@ class TextChannel extends GuildChannel { return this.client.getChannelInvites.call(this.client, this.id); } + /** + * Get joined private archived threads in this channel + * @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 + */ + getJoinedPrivateArchivedThreads(options) { + return this.client.getJoinedPrivateArchivedThreads.call(this.client, this.id, options); + } + /** * Get a previous message in the channel * @arg {String} messageID The ID of the message diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js new file mode 100644 index 000000000..b1a0b060f --- /dev/null +++ b/lib/structures/ThreadChannel.js @@ -0,0 +1,80 @@ +"use strict"; + +const Collection = require("../util/Collection"); +const GuildChannel = require("./GuildChannel"); +const Message = require("./Message"); + +/** +* Represents a thread channel. You also probably want to look at NewsThreadChannel, PublicThreadChannel, and PrivateThreadChannel. See GuildChannel for extra properties. +* @extends GuildChannel +* @prop {String} lastMessageID The ID of the last message in this channel +* @prop {Number} memberCount An approximate number of users in the thread (stops at 50) +* @prop {Number} messageCount An approximate number of messages in the thread (stops at 50) +* @prop {Collection} messages Collection of Messages in this channel +* @prop {String} ownerID The ID of the user that created the thread +* @prop {Number} rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled +* @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 {String?} threadMetadata.archiverID The ID of the user that last (un)archived the thread +* @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 {Object?} member Thread member for the current user, if they have joined the thread +* @prop {Number} member.flags The user's thread settings +* @prop {String} member.id The ID of the thread +* @prop {Number} member.joinTimestamp The time the user last joined the thread +* @prop {String} member.userID The ID of the userx +*/ +class ThreadChannel extends GuildChannel { + constructor(data, client, messageLimit) { + super(data, client); + this.messages = new Collection(Message, messageLimit == null ? client.options.messageLimit : messageLimit); + this.lastMessageID = data.last_message_id || null; + this.member = data.member; // TODO Class ThreadMember + } + + update(data) { + super.update(data); + if(data.rate_limit_per_user !== undefined) { + this.rateLimitPerUser = data.rate_limit_per_user; + } + if(data.thread_metadata !== undefined) { + 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 + }; + } + } + + + /** + * Get a list of members that are part of this thread channel + * @returns {Promise>} // TODO Class ThreadMember + */ + getMembers() { + return this.client.getThreadMembers.call(this.client, this.id); + } + + /** + * Join a thread + * @arg {String} [userID="@me"] The user ID of the user joining + * @returns {Promise} + */ + join(userID) { + return this.client.joinThread.call(this.client, this.id, userID); + } + + /** + * Leave a thread + * @arg {String} [userID="@me"] The user ID of the user leaving + * @returns {Promise} + */ + leave(userID) { + return this.client.leaveThread.call(this.client, this.id, userID); + } +} + +module.exports = ThreadChannel; From 22bf440bce08992fdb446387b125825430c98ffd Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 09:31:34 +0100 Subject: [PATCH 04/90] Typings cleanup --- index.d.ts | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/index.d.ts b/index.d.ts index de2286995..eb3ba6aa9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,9 +20,10 @@ declare namespace Eris { // Channel type AnyChannel = AnyGuildChannel | PrivateChannel; type AnyGuildChannel = GuildTextableChannel | AnyVoiceChannel | CategoryChannel | StoreChannel; + type AnyThreadChannel = NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel; type AnyVoiceChannel = VoiceChannel | StageChannel; type ChannelTypes = Constants["ChannelTypes"][keyof Constants["ChannelTypes"]]; - type GuildTextableChannel = TextChannel | NewsChannel; + type GuildTextableChannel = TextChannel | NewsChannel | AnyThreadChannel; type InviteChannel = InvitePartialChannel | Exclude; type PossiblyUncachedTextable = Textable | Uncached; type PossiblyUncachedTextableChannel = TextableChannel | Uncached; @@ -994,6 +995,21 @@ declare namespace Eris { premium_subscriber?: true; } + // Thread + interface ArchivedThreads { + hasMore: boolean; + members: unknown[]; // TODO Thread member + threads: T[]; + } + interface CreateThreadOptions { + autoArchiveDuration: number; + name: string; + } + interface GetArchivedThreadsOptions { + before?: Date; + limit?: number; + } + // Voice interface UncachedMemberVoiceState { id: string; @@ -1641,8 +1657,8 @@ declare namespace Eris { createGuildFromTemplate(code: string, name: string, icon?: string): Promise; createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createMessage(channelID: string, content: MessageContent, file?: MessageFile | MessageFile[]): Promise; - createPrivateThread(channelID: string, options: unknown): Promise; // TODO Create thread options - createPublicThread(channelID: string, options: unknown): Promise; // TODO Create thread options + createPrivateThread(channelID: string, options: CreateThreadOptions): Promise; + createPublicThread(channelID: string, options: CreateThreadOptions): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; @@ -1728,8 +1744,9 @@ 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<(NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[]>; // TODO Type alias - getArchivedThreads(channelID: string, type: "public" | "private", options?: unknown): Promise; // TODO Get archived thread options and return object + 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; @@ -1758,7 +1775,7 @@ declare namespace Eris { getGuildWidgetSettings(guildID: string): Promise; getInvite(inviteID: string, withCounts?: false): Promise>; getInvite(inviteID: string, withCounts: true): Promise>; - getJoinedPrivateArchivedThreads(channelID: string, options?: unknown): Promise; // TODO Get archived thread options and return object + getJoinedPrivateArchivedThreads(channelID: string, options?: GetArchivedThreadsOptions): Promise>; getMessage(channelID: string, messageID: string): Promise; getMessageReaction(channelID: string, messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -1769,7 +1786,7 @@ declare namespace Eris { getOAuthApplication(appID?: string): Promise; getPins(channelID: string): Promise; getPruneCount(guildID: string, options?: GetPruneOptions): Promise; - getRESTChannel(channelID: string): Promise; // TODO Type alias + getRESTChannel(channelID: string): Promise; getRESTGuild(guildID: string, withCounts?: boolean): Promise; getRESTGuildChannels(guildID: string): Promise; getRESTGuildEmoji(guildID: string, emojiID: string): Promise; @@ -2330,7 +2347,7 @@ declare namespace Eris { addReaction(reaction: string): Promise; /** @deprecated */ addReaction(reaction: string, userID: string): Promise; - createPublicThread(options: unknown): Promise; // TODO Create thread options + createPublicThread(options: CreateThreadOptions): Promise; crosspost(): Promise : never>; delete(reason?: string): Promise; deleteWebhook(token: string): Promise; @@ -2605,17 +2622,18 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; - createPrivateThread(options: unknown): Promise; // TODO Create thread options - createPublicThread(messageID: string, options: unknown): Promise; // TODO Create thread options + createPrivateThread(options: CreateThreadOptions): Promise; + createPublicThread(messageID: string, options: CreateThreadOptions): 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: MessageContent): Promise>; - getActiveThreads(): Promise<(NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[]>; // Type alias - getArchivedThreads(type: "public" | "private", options: unknown): Promise; // TODO Get archived thread options and return object + getActiveThreads(): Promise; + getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; + getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; getInvites(): Promise<(Invite<"withMetadata", TextChannel>)[]>; - getJoinedPrivateArchivedThreads(options: unknown): Promise; // TODO Get archived thread options and return object + getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions): Promise>; getMessage(messageID: string): Promise>; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ From 713a43b4fb535fe7d46b6c084586ad35e616f265 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:05:17 +0100 Subject: [PATCH 05/90] Fix some stuff --- lib/structures/NewsThreadChannel.js | 1 - lib/structures/PrivateThreadChannel.js | 1 - lib/structures/PublicThreadChannel.js | 1 - lib/structures/ThreadChannel.js | 26 +++++++++++++++++++++++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/structures/NewsThreadChannel.js b/lib/structures/NewsThreadChannel.js index 8cce63878..7eb981f22 100644 --- a/lib/structures/NewsThreadChannel.js +++ b/lib/structures/NewsThreadChannel.js @@ -9,7 +9,6 @@ const ThreadChannel = require("./ThreadChannel"); class NewsThreadChannel extends ThreadChannel { constructor(data, guild, messageLimit) { super(data, guild, messageLimit); - this.update(data); } } diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js index 0ffa1d13a..f81f0e017 100644 --- a/lib/structures/PrivateThreadChannel.js +++ b/lib/structures/PrivateThreadChannel.js @@ -9,7 +9,6 @@ const ThreadChannel = require("./ThreadChannel"); class PrivateThreadChannel extends ThreadChannel { constructor(data, guild, messageLimit) { super(data, guild, messageLimit); - this.update(data); } } diff --git a/lib/structures/PublicThreadChannel.js b/lib/structures/PublicThreadChannel.js index ecbebbe54..1fe71ecc4 100644 --- a/lib/structures/PublicThreadChannel.js +++ b/lib/structures/PublicThreadChannel.js @@ -9,7 +9,6 @@ const ThreadChannel = require("./ThreadChannel"); class PublicThreadChannel extends ThreadChannel { constructor(data, guild, messageLimit) { super(data, guild, messageLimit); - this.update(data); } } diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index b1a0b060f..25684d467 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -30,11 +30,18 @@ class ThreadChannel extends GuildChannel { super(data, client); this.messages = new Collection(Message, messageLimit == null ? client.options.messageLimit : messageLimit); this.lastMessageID = data.last_message_id || null; - this.member = data.member; // TODO Class ThreadMember + this.ownerID = data.ownerID; + this.update(data); } update(data) { super.update(data); + if(data.member_count !== undefined) { + this.memberCount = data.member_count; + } + if(data.message_count !== undefined) { + this.messageCount = data.message_count; + } if(data.rate_limit_per_user !== undefined) { this.rateLimitPerUser = data.rate_limit_per_user; } @@ -47,6 +54,9 @@ class ThreadChannel extends GuildChannel { locked: data.thread_metadata.locked }; } + if(data.member !== undefined) { + this.member = data.member; // TODO Class ThreadMember + } } @@ -75,6 +85,20 @@ class ThreadChannel extends GuildChannel { leave(userID) { return this.client.leaveThread.call(this.client, this.id, userID); } + + toJSON(props = []) { + return super.toJSON([ + "lastMessageID", + "memberCount", + "messageCount", + "messages", + "ownerID", + "rateLimitPerUser", + "threadMetadata", + "member", + ...props + ]); + } } module.exports = ThreadChannel; From 552b7de89997a8f00315be10bf899fbedaf604ef Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:06:22 +0100 Subject: [PATCH 06/90] ThreadMember class --- esm.mjs | 1 + index.d.ts | 18 +++++++++++++++++- index.js | 1 + lib/structures/ThreadMember.js | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 lib/structures/ThreadMember.js diff --git a/esm.mjs b/esm.mjs index e6adea5d4..c4926271c 100644 --- a/esm.mjs +++ b/esm.mjs @@ -43,6 +43,7 @@ export const { StoreChannel, TextChannel, ThreadChannel, + ThreadMember, UnavailableGuild, User, VERSION, diff --git a/index.d.ts b/index.d.ts index eb3ba6aa9..35f5aa640 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1009,6 +1009,13 @@ declare namespace Eris { before?: Date; limit?: number; } + interface ThreadMetadata { + archiveTimestamp: number; + archived: boolean; + archiverID?: string; + autoArchiveDuration: number; + locked?: boolean; + } // Voice interface UncachedMemberVoiceState { @@ -2662,7 +2669,7 @@ declare namespace Eris { messages: Collection; ownerID: string; rateLimitPerUser: number; - threadMetadata: unknown; // TODO Thread metadata + threadMetadata: ThreadMetadata; member?: unknown; // TODO Thread member constructor(data: BaseData, client: Client, messageLimit?: number); getMembers(): Promise; // TODO Thread member @@ -2670,6 +2677,15 @@ declare namespace Eris { leave(userID?: string): Promise; } + export class ThreadMember extends Base { + client: Client; + threadID: string; + joinTimestamp: number; + constructor(data: BaseData, client: Client); + update(data: BaseData): void; + leave(): Promise; + } + export class UnavailableGuild extends Base { createdAt: number; id: string; diff --git a/index.js b/index.js index dda1c198f..f09f3eb60 100644 --- a/index.js +++ b/index.js @@ -44,6 +44,7 @@ Eris.SharedStream = require("./lib/voice/SharedStream"); Eris.StoreChannel = require("./lib/structures/StoreChannel"); Eris.TextChannel = require("./lib/structures/TextChannel"); Eris.ThreadChannel = require("./lib/structures/ThreadChannel"); +Eris.ThreadMember = require("./lib/structures/ThreadMember"); Eris.UnavailableGuild = require("./lib/structures/UnavailableGuild"); Eris.User = require("./lib/structures/User"); Eris.VERSION = require("./package.json").version; diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js new file mode 100644 index 000000000..37abb58c8 --- /dev/null +++ b/lib/structures/ThreadMember.js @@ -0,0 +1,32 @@ +"use strict"; + +const Base = require("./Base"); + +class ThreadMember extends Base { + constructor(data, client) { + super(data.user_id); + this.client = client; + this.threadID = data.id; + this.joinTimestamp = Date.parse(data.join_timestamp); + } + + update(data) { + if(data.flags !== undefined) { + this.flags = data.flags; + } + } + + leave() { + return this.client.leaveThread.call(this.client, this.threadID, this.id); + } + + toJSON(props = []) { + return super.toJSON([ + "threadID", + "joinTimestamp", + ...props + ]); + } +} + +module.exports = ThreadMember; From 5d433aa898d3df0db026f2355f0333c37f374885 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:10:53 +0100 Subject: [PATCH 07/90] References for ThreadMember --- index.d.ts | 8 ++++---- lib/Client.js | 9 +++++---- lib/structures/ThreadChannel.js | 5 +++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index 35f5aa640..6b9e29d0d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -998,7 +998,7 @@ declare namespace Eris { // Thread interface ArchivedThreads { hasMore: boolean; - members: unknown[]; // TODO Thread member + members: ThreadMember[]; threads: T[]; } interface CreateThreadOptions { @@ -1842,7 +1842,7 @@ declare namespace Eris { status: number; }[]>; getSelfSettings(): Promise; - getThreadMembers(channelID: string): Promise; // TODO Thread member + getThreadMembers(channelID: string): Promise; getUserProfile(userID: string): Promise; getVoiceRegions(guildID?: string): Promise; getWebhook(webhookID: string, token?: string): Promise; @@ -2670,9 +2670,9 @@ declare namespace Eris { ownerID: string; rateLimitPerUser: number; threadMetadata: ThreadMetadata; - member?: unknown; // TODO Thread member + member?: ThreadMember; constructor(data: BaseData, client: Client, messageLimit?: number); - getMembers(): Promise; // TODO Thread member + getMembers(): Promise; join(userID?: string): Promise; leave(userID?: string): Promise; } diff --git a/lib/Client.js b/lib/Client.js index 838237593..b10651ac0 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -21,6 +21,7 @@ const Relationship = require("./structures/Relationship"); const RequestHandler = require("./rest/RequestHandler"); const Role = require("./structures/Role"); const ShardManager = require("./gateway/ShardManager"); +const ThreadMember = require("./structures/ThreadMember"); const UnavailableGuild = require("./structures/UnavailableGuild"); const User = require("./structures/User"); const VoiceConnectionManager = require("./voice/VoiceConnectionManager"); @@ -1643,7 +1644,7 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED(channelID, type), true, options).then((response) => { return { hasMore: response.has_more, - members: response.members, // TODO Class ThreadMember + members: response.members.map((member) => new ThreadMember(member, this)), threads: response.threads.map((thread) => Channel.from(thread, this)) }; }); @@ -1943,7 +1944,7 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { return { hasMore: response.has_more, - members: response.members, // TODO Class ThreadMember + members: response.members.map((member) => new ThreadMember(member, this)), threads: response.threads.map((thread) => Channel.from(thread, this)) }; }); @@ -2304,10 +2305,10 @@ class Client extends EventEmitter { /** * Get a list of members that are part of a thread channel * @arg {String} channelID The ID of the thread channel - * @returns {Promise>} // TODO Class ThreadMember + * @returns {Promise>} */ getThreadMembers(channelID) { - return this.requestHandler.request("GET", Endpoints.GUILD_THREAD_MEMBERS(channelID), true); + return this.requestHandler.request("GET", Endpoints.GUILD_THREAD_MEMBERS(channelID), true).then((members) => members.map((member) => new ThreadMember(member, this))); } /** diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 25684d467..6b9c6a036 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -3,6 +3,7 @@ const Collection = require("../util/Collection"); const GuildChannel = require("./GuildChannel"); const Message = require("./Message"); +const ThreadMember = require("./ThreadMember"); /** * Represents a thread channel. You also probably want to look at NewsThreadChannel, PublicThreadChannel, and PrivateThreadChannel. See GuildChannel for extra properties. @@ -55,14 +56,14 @@ class ThreadChannel extends GuildChannel { }; } if(data.member !== undefined) { - this.member = data.member; // TODO Class ThreadMember + this.member = new ThreadMember(data.member, this.client); } } /** * Get a list of members that are part of this thread channel - * @returns {Promise>} // TODO Class ThreadMember + * @returns {Promise>} */ getMembers() { return this.client.getThreadMembers.call(this.client, this.id); From 270e1f74f6817dbe89088cf90a545ffb705f4641 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:24:17 +0100 Subject: [PATCH 08/90] Add threads prop to guild --- index.d.ts | 2 ++ lib/Client.js | 2 ++ lib/structures/Guild.js | 12 ++++++++++++ 3 files changed, 16 insertions(+) diff --git a/index.d.ts b/index.d.ts index 6b9e29d0d..48cbbd7bc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1529,6 +1529,7 @@ declare namespace Eris { requestHandler: RequestHandler; shards: ShardManager; startTime: number; + threadGuildMap: { [s: string]: string }; unavailableGuilds: Collection; uptime: number; user: ExtendedUser; @@ -2051,6 +2052,7 @@ declare namespace Eris { splashURL: string | null; systemChannelFlags: number; systemChannelID: string | null; + threads: Collection; unavailable: boolean; vanityURL: string | null; verificationLevel: VerificationLevel; diff --git a/lib/Client.js b/lib/Client.js index b10651ac0..04aa09aff 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -66,6 +66,7 @@ const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); * @prop {RequestHandler} requestHandler The request handler the client will use * @prop {Collection} shards Collection of shards Eris is using * @prop {Number} startTime Timestamp of bot ready event +* @prop {Object} threadGuildMap Object mapping thread channel IDs to guild IDs * @prop {Collection} unavailableGuilds Collection of unavailable guilds the bot is in * @prop {Number} uptime How long in milliseconds the bot has been up for * @prop {ExtendedUser} user The bot user @@ -202,6 +203,7 @@ class Client extends EventEmitter { this.startTime = 0; this.lastConnect = 0; this.channelGuildMap = {}; + this.threadGuildMap = {}; this.shards = new ShardManager(this); this.groupChannels = new Collection(GroupChannel); this.guilds = new Collection(Guild); diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 93db44a81..bdeb56bc2 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -10,6 +10,7 @@ const Role = require("./Role"); const VoiceState = require("./VoiceState"); const Permission = require("./Permission"); const {Permissions} = require("../Constants"); +const ThreadChannel = require("./ThreadChannel"); /** * Represents a guild @@ -62,6 +63,7 @@ const {Permissions} = require("../Constants"); * @prop {String?} splashURL The URL of the guild's splash image * @prop {Number} systemChannelFlags The flags for the system channel * @prop {String?} systemChannelID The ID of the default channel for system messages (built-in join messages and boost messages) +* @prop {Collection} threads Collection of threads that the current user has permission to view * @prop {Boolean} unavailable Whether the guild is unavailable or not * @prop {String?} vanityURL The vanity URL of the guild (VIP only) * @prop {Number} verificationLevel The guild verification level @@ -81,6 +83,7 @@ class Guild extends Base { this.joinedAt = Date.parse(data.joined_at); this.voiceStates = new Collection(VoiceState); this.channels = new Collection(GuildChannel); + this.threads = new Collection(ThreadChannel); this.members = new Collection(Member); this.memberCount = data.member_count; this.roles = new Collection(Role); @@ -134,6 +137,15 @@ class Guild extends Base { client.channelGuildMap[channel.id] = this.id; } } + if(data.threads) { + for(const threadData of data.threads) { + threadData.guild_id = this.id; + const channel = Channel.from(threadData, client); + channel.guild = this; + this.threads.add(channel, client); + client.threadGuildMap[channel.id] = this.id; + } + } if(data.members) { for(const member of data.members) { From 1305ed6299336c54a8619e2178214a49dce8971c Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:27:10 +0100 Subject: [PATCH 09/90] Documentation update --- lib/structures/GuildChannel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index b20082f57..22f299809 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -82,7 +82,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) + * @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 {String} [reason] The reason to be displayed in audit logs * @returns {Promise} */ From e7099fac6e41e7788017b6641496e02cef842e8e Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 10:44:10 +0100 Subject: [PATCH 10/90] Doc update --- lib/structures/GuildChannel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 22f299809..ef93e9310 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 +* @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 {Collection} permissionOverwrites Collection of PermissionOverwrites in this channel * @prop {Number} position The position of the channel */ From b7ac7bd39870274e07294127e046a43c405a9172 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 13:48:17 +0100 Subject: [PATCH 11/90] Add support for stage channels in getChannel --- lib/Client.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Client.js b/lib/Client.js index 04aa09aff..db1b80574 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1666,7 +1666,7 @@ class Client extends EventEmitter { /** * Get a Channel object from a channel ID * @arg {String} channelID The ID of the channel - * @returns {CategoryChannel | GroupChannel | PrivateChannel | TextChannel | VoiceChannel | NewsChannel} + * @returns {CategoryChannel | GroupChannel | PrivateChannel | TextChannel | VoiceChannel | NewsChannel | NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} */ getChannel(channelID) { if(!channelID) { @@ -1676,6 +1676,9 @@ class Client extends EventEmitter { if(this.channelGuildMap[channelID] && this.guilds.get(this.channelGuildMap[channelID])) { return this.guilds.get(this.channelGuildMap[channelID]).channels.get(channelID); } + if(this.threadGuildMap[channelID] && this.guilds.get(this.threadGuildMap[channelID])) { + return this.guilds.get(this.threadGuildMap[channelID]).channels.get(channelID); + } return this.privateChannels.get(channelID) || this.groupChannels.get(channelID); } From 54ef7a49fa247f72fc42f9b620ee8052d0e4d722 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 14:00:47 +0100 Subject: [PATCH 12/90] Thread events (so far) --- index.d.ts | 7 ++++ lib/gateway/Shard.js | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/index.d.ts b/index.d.ts index 48cbbd7bc..470e71c52 100644 --- a/index.d.ts +++ b/index.d.ts @@ -490,6 +490,11 @@ declare namespace Eris { permissions: Permission; position: number; } + interface OldThread { + name: string; + rateLimitPerUser: number; + threadMetadata: ThreadMetadata; + } interface OldVoiceState { deaf: boolean; mute: boolean; @@ -556,6 +561,8 @@ declare namespace Eris { event: "relationshipUpdate", listener: (relationship: Relationship, oldRelationship: { type: number }) => void ): T; + (event: "threadCreate" | "threadDelete", listener: (channel: AnyThreadChannel) => void): T; + (event: "threadUpdate", listener: (channel: AnyThreadChannel, oldChannel: OldThread) => void): T; (event: "typingStart", listener: (channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member) => void): T; (event: "typingStart", listener: (channel: PrivateChannel | Uncached, user: User | Uncached, member: null) => void): T; ( diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 99a641251..8aa4ecf43 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -14,6 +14,7 @@ const ExtendedUser = require("../structures/ExtendedUser"); const User = require("../structures/User"); const Invite = require("../structures/Invite"); const Constants = require("../Constants"); +const ThreadChannel = require("../structures/ThreadChannel"); const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSocket") : require("ws"); @@ -2069,6 +2070,95 @@ class Shard extends EventEmitter { this.client.userGuildSettings[packet.d.guild_id] = packet.d; break; } + case "THREAD_CREATE": { + const channel = Channel.from(packet.d, this.client); + if(!channel.guild) { + channel.guild = this.client.guilds.get(packet.d.guild_id); + if(!channel.guild) { + this.emit("debug", `Received THREAD_CREATE for channel in missing guild ${packet.d.guild_id}`); + break; + } + } + channel.guild.threads.add(channel, this.client); + this.client.threadGuildMap[packet.d.id] = packet.d.guild_id; + /** + * Fired when a channel is created + * @event Client#threadCreate + * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The channel + */ + this.emit("threadCreate", channel); + break; + } + case "THREAD_UPDATE": { + const channel = this.client.getChannel(packet.d.id); + if(!channel) { + break; + } + if(!(channel instanceof ThreadChannel)) { + this.emit("warn", `Unexpected THREAD_UPDATE for channel ${packet.d.id} with type ${channel.type}`); + break; + } + const oldChannel = { + name: channel.name, + rateLimitPerUser: channel.rateLimitPerUser, + threadMetadata: channel.threadMetadata + }; + channel.update(packet.d); + + /** + * Fired when a thread channel is updated + * @event Client#threadUpdate + * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel + * @prop {Object} oldChannel + * @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 + * @prop {Number} oldChannel.threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity + * @prop {Boolean} oldChannel.threadMetadata.archived Whether the thread is archived + * @prop {String?} oldChannel.threadMetadata.archiverID The ID of the user that last (un)archived the thread + * @prop {Number} oldChannel.threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 + * @prop {Boolean?} oldChannel.threadMetadata.locked Whether the thread is locked + */ + this.emit("threadUpdate", channel, oldChannel); + break; + } + case "THREAD_DELETE": { + delete this.client.threadGuildMap[packet.d.id]; + const guild = this.client.guilds.get(packet.d.guild_id); + if(!guild) { + this.emit("debug", `Missing guild ${packet.d.guild_id} in THREAD_DELETE`); + break; + } + const channel = guild.threads.remove(packet.d); + if(!channel) { + break; + } + /** + * Fired when a thread channel is deleted + * @event Client#threadDelete + * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The channel + */ + this.emit("threadDelete", channel); + break; + } + case "THREAD_LIST_SYNC": { + // TODO Need to double check this + break; + } + case "THREAD_MEMBER_UPDATE": { + // TODO Need to sort out caching Thread Members + break; + } + case "THREAD_MEMBERS_UPDATE": { + const channel = this.cleint.getChannel(packet.d.id); + if(!channel) { + this.emit("debug", `Missing channel ${packet.d.id} in THREAD_MEMBERS_UPDATE`); + break; + } + channel.update(packet.d); + // TODO Other fields? + break; + } case "MESSAGE_ACK": // Ignore these case "GUILD_INTEGRATIONS_UPDATE": case "USER_SETTINGS_UPDATE": From 8a2bc9094a505e7d49abd7cc169f4c074e33f8e7 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 23:10:29 +0100 Subject: [PATCH 13/90] Discord messed up case --- lib/structures/ThreadMember.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js index 37abb58c8..8a2c17655 100644 --- a/lib/structures/ThreadMember.js +++ b/lib/structures/ThreadMember.js @@ -6,7 +6,7 @@ class ThreadMember extends Base { constructor(data, client) { super(data.user_id); this.client = client; - this.threadID = data.id; + this.threadID = data.thread_id || data.id; // Thanks Discord this.joinTimestamp = Date.parse(data.join_timestamp); } From 8888a27430a92e136b1590809df63c18d7ea7409 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 23:11:20 +0100 Subject: [PATCH 14/90] Add ThreadMember cache --- lib/structures/ThreadChannel.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 6b9c6a036..4e86b8ea7 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -12,6 +12,7 @@ const ThreadMember = require("./ThreadMember"); * @prop {Number} memberCount An approximate number of users in the thread (stops at 50) * @prop {Number} messageCount An approximate number of messages in the thread (stops at 50) * @prop {Collection} messages Collection of Messages in this channel +* @prop {Collection} members Collection of members in this channel * @prop {String} ownerID The ID of the user that created the thread * @prop {Number} rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled * @prop {Object} threadMetadata Metadata for the thread @@ -30,6 +31,7 @@ class ThreadChannel extends GuildChannel { constructor(data, client, messageLimit) { super(data, client); this.messages = new Collection(Message, messageLimit == null ? client.options.messageLimit : messageLimit); + this.members = new Collection(ThreadMember); this.lastMessageID = data.last_message_id || null; this.ownerID = data.ownerID; this.update(data); From 7eb457c7d23a43efb27f8e2e124536408a97482d Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 23:14:38 +0100 Subject: [PATCH 15/90] Doc fixes --- index.d.ts | 3 ++- lib/structures/ThreadChannel.js | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 470e71c52..47330f521 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2673,13 +2673,14 @@ declare namespace Eris { export class ThreadChannel extends GuildChannel { lastMessageID: string; + member?: ThreadMember; memberCount: number; + members: Collection; messageCount: number; messages: Collection; ownerID: string; rateLimitPerUser: number; threadMetadata: ThreadMetadata; - member?: ThreadMember; constructor(data: BaseData, client: Client, messageLimit?: number); getMembers(): Promise; join(userID?: string): Promise; diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 4e86b8ea7..201563961 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -9,10 +9,15 @@ const ThreadMember = require("./ThreadMember"); * Represents a thread channel. You also probably want to look at NewsThreadChannel, PublicThreadChannel, and PrivateThreadChannel. See GuildChannel for extra properties. * @extends GuildChannel * @prop {String} lastMessageID The ID of the last message in this channel +* @prop {Object?} member Thread member for the current user, if they have joined the thread +* @prop {Number} member.flags The user's thread settings +* @prop {String} member.id The ID of the thread +* @prop {Number} member.joinTimestamp The time the user last joined the thread +* @prop {String} member.userID The ID of the user * @prop {Number} memberCount An approximate number of users in the thread (stops at 50) +* @prop {Collection} members Collection of members in this channel * @prop {Number} messageCount An approximate number of messages in the thread (stops at 50) * @prop {Collection} messages Collection of Messages in this channel -* @prop {Collection} members Collection of members in this channel * @prop {String} ownerID The ID of the user that created the thread * @prop {Number} rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled * @prop {Object} threadMetadata Metadata for the thread @@ -21,11 +26,6 @@ const ThreadMember = require("./ThreadMember"); * @prop {String?} threadMetadata.archiverID The ID of the user that last (un)archived the thread * @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 {Object?} member Thread member for the current user, if they have joined the thread -* @prop {Number} member.flags The user's thread settings -* @prop {String} member.id The ID of the thread -* @prop {Number} member.joinTimestamp The time the user last joined the thread -* @prop {String} member.userID The ID of the userx */ class ThreadChannel extends GuildChannel { constructor(data, client, messageLimit) { From e7a2401c1995c6da8eaa4af9cbc5c0efd894f3b6 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 25 May 2021 23:20:34 +0100 Subject: [PATCH 16/90] Complete gateway events --- index.d.ts | 6 +++++ lib/gateway/Shard.js | 61 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 47330f521..0cc57c202 100644 --- a/index.d.ts +++ b/index.d.ts @@ -495,6 +495,9 @@ declare namespace Eris { rateLimitPerUser: number; threadMetadata: ThreadMetadata; } + interface OldThreadMember { + flags: number; + } interface OldVoiceState { deaf: boolean; mute: boolean; @@ -562,6 +565,9 @@ declare namespace Eris { listener: (relationship: Relationship, oldRelationship: { type: number }) => void ): T; (event: "threadCreate" | "threadDelete", listener: (channel: AnyThreadChannel) => void): T; + (event: "threadListSync", listener: (guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]) => void): T; + (event: "threadMembersUpdate", listener: (channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]) => void): T; + (event: "threadMemberUpdate", listener: (channel: AnyThreadChannel, member: ThreadMember, oldMember: OldThreadMember) => void): T; (event: "threadUpdate", listener: (channel: AnyThreadChannel, oldChannel: OldThread) => void): T; (event: "typingStart", listener: (channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member) => void): T; (event: "typingStart", listener: (channel: PrivateChannel | Uncached, user: User | Uncached, member: null) => void): T; diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 8aa4ecf43..fbba69105 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2142,11 +2142,51 @@ class Shard extends EventEmitter { break; } case "THREAD_LIST_SYNC": { - // TODO Need to double check this + const guild = this.client.guilds.get(packet.d.guild_id); + if(!guild) { + this.emit("debug", `Missing guild ${packet.d.guild_id} in THREAD_LIST_SYNC`); + break; + } + const deletedThreads = (packet.d.channel_ids || guild.threads.map((c) => c.id)) // REVIEW Is this a good name? + .filter((c) => !packet.d.threads.some((t) => t.id === c)).map((id) => guild.threads.remove({id}) || {id}); + const activeThreads = packet.d.threads.map((t) => guild.threads.update(t, this.client)); + const joinedThreadsMember = packet.d.members.map((m) => guild.threads.get(m.id).members.update(m, this.client)); + /** + * Fired when the current user gains access to a channel + * @event Client#threadListSync + * @prop {Guild} guild The guild where threads are being synced + * @prop {Array} deletedThreads An array of synced threads that the current user no longer has access to. If a thread channel is uncached, it will be an object with an `id` key. No other property is guaranteed + * @prop {Array} activeThreads An array of synced active threads that the current user can access + * @prop {Array} joinedThreadsMember An array of thread member objects where the current user has been added in a synced thread channel + */ + this.emit("threadListSync", guild, deletedThreads, activeThreads, joinedThreadsMember); break; } case "THREAD_MEMBER_UPDATE": { - // TODO Need to sort out caching Thread Members + const channel = this.client.getChannel(packet.d.id); + if(!channel) { + this.emit("debug", `Missing channel ${packet.d.id} in THREAD_MEMBER_UPDATE`); + break; + } + let oldMember = null; + // Thanks Discord + packet.d.thread_id = packet.d.id; + let member = channel.members.get((packet.d.id = packet.d.user_id)); + if(member) { + oldMember = { + flags: member.flags + }; + } + member = channel.members.update(packet.d, this.client); + /** + * Fired when a thread member is updated + * @event Client#threadMemberUpdate + * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The channel + * @prop {ThreadMember} member The updated thread member + * @prop {Object} oldMember The old thread member data + * @prop {Number} oldMember.flags User thread settings + */ + this.emit("threadMemberUpdate", channel, member, oldMember); break; } case "THREAD_MEMBERS_UPDATE": { @@ -2156,7 +2196,22 @@ class Shard extends EventEmitter { break; } channel.update(packet.d); - // TODO Other fields? + let removedMembers; + let addedMembers; + if(packet.d.removed_member_ids) { + 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)); + } + /** + * 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 + * @event Client#threadMembersUpdate + * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The thread channel + * @prop {Array} removedMembers An array of members that were removed from the thread channel. If a member is uncached, it will be an object with an `id` key. No other property is guaranteed + * @prop {Array} addedMembers An array of members that were added to the thread channel + */ + this.emit("threadMembersUpdate", channel, removedMembers, addedMembers); break; } case "MESSAGE_ACK": // Ignore these From c6a3fd03bddf5d2a582c148a77211a037a62e073 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 09:19:33 +0100 Subject: [PATCH 17/90] Support uploading new attachments on message edit --- index.d.ts | 38 ++++++++++++++++++-------------- lib/Client.js | 9 +++++--- lib/structures/Message.js | 11 +++++---- lib/structures/PrivateChannel.js | 3 +++ lib/structures/TextChannel.js | 3 +++ 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/index.d.ts b/index.d.ts index 0cc57c202..b68f389f3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -53,20 +53,9 @@ declare namespace Eris { type VerificationLevel = 0 | 1 | 2 | 3 | 4; // Message - type AdvancedMessageContent = { - allowedMentions?: AllowedMentions; - content?: string; - /** @deprecated */ - embed?: EmbedOptions; - embeds?: EmbedOptions[]; - flags?: number; - messageReference?: MessageReferenceReply; - /** @deprecated */ - messageReferenceID?: string; - tts?: boolean; - }; type ImageFormat = "jpg" | "jpeg" | "png" | "gif" | "webp"; type MessageContent = string | AdvancedMessageContent; + type MessageContentEdit = string | AdvancedMessageContentEdit; type MFALevel = 0 | 1; type PossiblyUncachedMessage = Message | { channel: TextableChannel | { id: string; guild?: Uncached }; guildID?: string; id: string }; type InteractionType = 1 | 2; @@ -178,7 +167,7 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise; deleteMessage(messageID: string, reason?: string): Promise; - editMessage(messageID: string, content: MessageContent): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise; getMessage(messageID: string): Promise; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -862,6 +851,21 @@ declare namespace Eris { command: Command; timeout: NodeJS.Timer; } + interface AdvancedMessageContent { + allowedMentions?: AllowedMentions; + content?: string; + /** @deprecated */ + embed?: EmbedOptions; + embeds?: EmbedOptions[]; + flags?: number; + messageReference?: MessageReferenceReply; + /** @deprecated */ + messageReferenceID?: string; + tts?: boolean; + } + interface AdvancedMessageContentEdit extends AdvancedMessageContent { + file?: MessageFile | MessageFile[]; + } interface AllowedMentions { everyone?: boolean; repliedUser?: boolean; @@ -1730,7 +1734,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; editNickname(guildID: string, nick: string, reason?: string): Promise; editRole(guildID: string, roleID: string, options: RoleOptions, reason?: string): Promise; // TODO not all options are available? editRolePosition(guildID: string, roleID: string, position: number): Promise; @@ -2392,7 +2396,7 @@ declare namespace Eris { createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; crosspostMessage(messageID: string): Promise>; - editMessage(messageID: string, content: MessageContent): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; follow(webhookChannelID: string): Promise; getInvites(): Promise<(Invite<"withMetadata", NewsChannel>)[]>; getMessage(messageID: string): Promise>; @@ -2446,7 +2450,7 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; deleteMessage(messageID: string, reason?: string): Promise; - editMessage(messageID: string, content: MessageContent): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -2650,7 +2654,7 @@ declare namespace Eris { deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; edit(options: Omit, reason?: string): Promise; - editMessage(messageID: string, content: MessageContent): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; getActiveThreads(): Promise; getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; diff --git a/lib/Client.js b/lib/Client.js index db1b80574..f288e49c7 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1274,6 +1274,9 @@ class Client extends EventEmitter { * @arg {String} content.content A content string * @arg {Object} [content.embed] [DEPRECATED] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure. Use `embeds` instead * @arg {Array} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure + * @arg {Object | Array} [content.file] A file object (or an Array of them) + * @arg {Buffer} content.file[].file A buffer containing file data + * @arg {String} content.file[].name What to name the file * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference * @returns {Promise} */ @@ -1295,7 +1298,7 @@ class Client extends EventEmitter { content.allowed_mentions = this._formatAllowedMentions(content.allowedMentions); } } - return this.requestHandler.request("PATCH", Endpoints.CHANNEL_MESSAGE(channelID, messageID), true, content).then((message) => new Message(message, this)); + return this.requestHandler.request("PATCH", Endpoints.CHANNEL_MESSAGE(channelID, messageID), true, content, content.file).then((message) => new Message(message, this)); } /** @@ -1531,8 +1534,8 @@ class Client extends EventEmitter { * @arg {String} [options.content=""] A content string * @arg {Array} [options.embeds] An array of Discord embeds * @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 {Buffer} options.file[].file A buffer containing file data + * @arg {String} options.file[].name What to name the file * @returns {Promise} */ editWebhookMessage(webhookID, token, messageID, options) { diff --git a/lib/structures/Message.js b/lib/structures/Message.js index f3e050fe2..fae970cf0 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -415,14 +415,17 @@ 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 {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 - * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference * @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default) * @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here. * @arg {Boolean | Array} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow. * @arg {Boolean | Array} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow. + * @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 + * @arg {Object | Array} [content.file] A file object (or an Array of them) + * @arg {Buffer} content.file[].file A buffer containing file data + * @arg {String} content.file[].name What to name the file + * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference * @returns {Promise} */ edit(content) { diff --git a/lib/structures/PrivateChannel.js b/lib/structures/PrivateChannel.js index 1eff5c616..7555f45b4 100644 --- a/lib/structures/PrivateChannel.js +++ b/lib/structures/PrivateChannel.js @@ -87,6 +87,9 @@ class PrivateChannel extends Channel { * @arg {Boolean} [content.disableEveryone] Whether to filter @everyone/@here or not (overrides default) * @arg {Object} [content.embed] [DEPRECATED] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure. Use `embeds` instead * @arg {Array} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure + * @arg {Object | Array} [content.file] A file object (or an Array of them) + * @arg {Buffer} content.file[].file A buffer containing file data + * @arg {String} content.file[].name What to name the file * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference * @returns {Promise} */ diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index a417c2256..048eecf7a 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -153,6 +153,9 @@ class TextChannel extends GuildChannel { * @arg {Boolean} [content.disableEveryone] Whether to filter @everyone/@here or not (overrides default) * @arg {Object} [content.embed] [DEPRECATED] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure. Use `embeds` instead * @arg {Array} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure + * @arg {Object | Array} [content.file] A file object (or an Array of them) + * @arg {Buffer} content.file[].file A buffer containing file data + * @arg {String} content.file[].name What to name the file * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference * @returns {Promise} */ From a78baca01240195d1849450accc6bbcc11035750 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 09:34:10 +0100 Subject: [PATCH 18/90] Support webhooks sending to thread channels --- index.d.ts | 5 +++-- lib/Client.js | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index b68f389f3..4c6468539 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1099,6 +1099,7 @@ declare namespace Eris { content?: string; embeds?: EmbedOptions[]; file?: MessageFile | MessageFile[]; + threadID?: string; tts?: boolean; username?: string; wait?: boolean; @@ -1764,8 +1765,8 @@ declare namespace Eris { secret: string, code: string ): Promise<{ backup_codes: { code: string; consumed: boolean }[]; token: string }>; - executeSlackWebhook(webhookID: string, token: string, options: Record & { auth?: boolean }): Promise; - executeSlackWebhook(webhookID: string, token: string, options: Record & { auth?: boolean; wait: true }): Promise>; + executeSlackWebhook(webhookID: string, token: string, options: Record & { auth?: boolean; threadID?: string }): Promise; + executeSlackWebhook(webhookID: string, token: string, options: Record & { auth?: boolean; threadID?: string; wait: true }): Promise>; executeWebhook(webhookID: string, token: string, options: WebhookPayload & { wait: true }): Promise>; executeWebhook(webhookID: string, token: string, options: WebhookPayload): Promise; followChannel(channelID: string, webhookChannelID: string): Promise; diff --git a/lib/Client.js b/lib/Client.js index f288e49c7..ba28e9235 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1571,6 +1571,7 @@ class Client extends EventEmitter { * @arg {String} token The token of the webhook * @arg {Object} options Slack webhook options * @arg {Boolean} [options.auth=false] Whether or not to authenticate with the bot token. + * @arg {String} [options.threadID] The ID of the thread channel in the webhook's channel to send the message to * @arg {Boolean} [options.wait=false] Whether to wait for the server to confirm the message create or not * @returns {Promise} */ @@ -1579,7 +1580,16 @@ class Client extends EventEmitter { options.wait = undefined; const auth = !!options.auth; options.auth = undefined; - return this.requestHandler.request("POST", Endpoints.WEBHOOK_TOKEN_SLACK(webhookID, token) + (wait ? "?wait=true" : ""), auth, options); + const threadID = options.threadID; + options.threadID = undefined; + let qs = ""; + if(wait) { + qs += "&wait=true"; + } + if(threadID) { + qs += "&thread_id=" + threadID; + } + return this.requestHandler.request("POST", Endpoints.WEBHOOK_TOKEN_SLACK(webhookID, token) + (qs ? "?" + qs : ""), auth, options); } /** @@ -1598,6 +1608,7 @@ 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 {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 @@ -1607,7 +1618,14 @@ class Client extends EventEmitter { if(!options.content && !options.file && !options.embeds) { return Promise.reject(new Error("No content, file, or embeds")); } - return this.requestHandler.request("POST", Endpoints.WEBHOOK_TOKEN(webhookID, token) + (options.wait ? "?wait=true" : ""), !!options.auth, { + let qs = ""; + if(options.wait) { + qs += "&wait=true"; + } + if(options.threadID) { + qs += "&thread_id=" + options.threadID; + } + return this.requestHandler.request("POST", Endpoints.WEBHOOK_TOKEN(webhookID, token) + (qs ? "?" + qs : ""), !!options.auth, { content: options.content, embeds: options.embeds, username: options.username, From f8c11eab0fb8c42b259d0c8d56828fac0ea5ab14 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 09:42:20 +0100 Subject: [PATCH 19/90] Thread documentation clarification --- index.d.ts | 5 +++-- lib/Client.js | 2 +- lib/structures/Message.js | 2 +- lib/structures/TextChannel.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index 4c6468539..0ad8560aa 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1684,7 +1684,7 @@ declare namespace Eris { createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createMessage(channelID: string, content: MessageContent, file?: MessageFile | MessageFile[]): Promise; createPrivateThread(channelID: string, options: CreateThreadOptions): Promise; - createPublicThread(channelID: string, options: CreateThreadOptions): Promise; + createPublicThread(channelID: string, options: CreateThreadOptions): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; @@ -2374,7 +2374,7 @@ declare namespace Eris { addReaction(reaction: string): Promise; /** @deprecated */ addReaction(reaction: string, userID: string): Promise; - createPublicThread(options: CreateThreadOptions): Promise; + createPublicThread(options: CreateThreadOptions): Promise; crosspost(): Promise : never>; delete(reason?: string): Promise; deleteWebhook(token: string): Promise; @@ -2396,6 +2396,7 @@ declare namespace Eris { type: 5; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + createPublicThread(messageID: string, options: CreateThreadOptions): Promise; crosspostMessage(messageID: string): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; follow(webhookChannelID: string): Promise; diff --git a/lib/Client.js b/lib/Client.js index ba28e9235..d327dccf6 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -662,7 +662,7 @@ class Client extends EventEmitter { * @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 {String} options.name The thread channel name - * @returns {Promise} + * @returns {Promise} */ createPublicThread(channelID, messageID, options) { return this.requestHandler.request("POST", Endpoints.GUILD_THREAD_PUBLIC(channelID, messageID), true, { diff --git a/lib/structures/Message.js b/lib/structures/Message.js index fae970cf0..4dc1c11a1 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -377,7 +377,7 @@ class Message extends Base { * @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 {String} options.name The thread channel name - * @returns {Promise} + * @returns {Promise} */ createPublicThread(options) { return this.client.createPublicThread.call(this.client, this.channel.id, this.id, options); diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 048eecf7a..9cf42f12f 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -103,7 +103,7 @@ class TextChannel extends GuildChannel { * @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 {String} options.name The thread channel name - * @returns {Promise} + * @returns {Promise} */ createPublicThread(messageID, options) { return this.client.createPublicThread.call(this.client, this.id, messageID, options); From c591b45b9a3957e123dc6853b7f357fba6dca71a Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 09:54:42 +0100 Subject: [PATCH 20/90] Extra newline --- lib/structures/Message.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 4dc1c11a1..92e9b7eae 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -48,7 +48,6 @@ const User = require("./User"); * @prop {Boolean} tts Whether to play the message using TTS or not * @prop {Number} type The type of the message * @prop {String?} webhookID ID of the webhook that sent the message - */ class Message extends Base { constructor(data, client) { From 1f20e386808519f044dcf6034c3c6f8f8aa4c82b Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 10:33:12 +0100 Subject: [PATCH 21/90] Expose stage instance --- esm.mjs | 2 ++ index.d.ts | 21 ++++++++++++++++ index.js | 2 ++ lib/Client.js | 43 +++++++++++++++++++++++++++++++++ lib/rest/Endpoints.js | 2 ++ lib/structures/StageChannel.js | 35 +++++++++++++++++++++++++++ lib/structures/StageInstance.js | 40 ++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+) create mode 100644 lib/structures/StageInstance.js diff --git a/esm.mjs b/esm.mjs index c4926271c..318949ffe 100644 --- a/esm.mjs +++ b/esm.mjs @@ -40,6 +40,8 @@ export const { SequentialBucket, Shard, SharedStream, + StageChannel, + StageInstance, StoreChannel, TextChannel, ThreadChannel, diff --git a/index.d.ts b/index.d.ts index 0ad8560aa..3806d565f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1035,6 +1035,9 @@ declare namespace Eris { } // Voice + interface EditStageInstanceOptions { + topic: string; + } interface UncachedMemberVoiceState { id: string; voiceState: OldVoiceState; @@ -1686,6 +1689,7 @@ declare namespace Eris { createPrivateThread(channelID: string, options: CreateThreadOptions): Promise; createPublicThread(channelID: string, options: CreateThreadOptions): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; + createStageInstance(channelID: string, topic: string): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -1700,6 +1704,7 @@ declare namespace Eris { deleteRole(guildID: string, roleID: string, reason?: string): Promise; deleteSelfConnection(platform: string, id: string): Promise; deleteSelfPremiumSubscription(): Promise; + deleteStageInstance(channelID: string): Promise; deleteUserNote(userID: string): Promise; deleteWebhook(webhookID: string, token?: string, reason?: string): Promise; deleteWebhookMessage(webhookID: string, token: string, messageID: string): Promise; @@ -1746,6 +1751,7 @@ declare namespace Eris { data: { friendSync: boolean; visibility: number } ): Promise; editSelfSettings(data: UserSettings): Promise; + editStageInstance(channelID: string, options: EditStageInstanceOptions): Promise; editStatus(status: Status, activities?: ActivityPartial[] | ActivityPartial): void; editStatus(activities?: ActivityPartial[] | ActivityPartial): void; editUserNote(userID: string, note: string): Promise; @@ -1861,6 +1867,7 @@ declare namespace Eris { status: number; }[]>; getSelfSettings(): Promise; + getStageInstance(channelID: string): Promise; getThreadMembers(channelID: string): Promise; getUserProfile(userID: string): Promise; getVoiceRegions(guildID?: string): Promise; @@ -2630,6 +2637,20 @@ declare namespace Eris { export class StageChannel extends VoiceChannel { topic?: string; type: 13; + createInstance(topic: string): Promise; + deleteInstance(): Promise; + editInstance(options: EditStageInstanceOptions): Promise; + getInstance(): Promise; + } + + export class StageInstance extends Base { + client: Client; + channel: StageChannel | Uncached; + guild: Guild | Uncached; + topic: string; + constructor(data: BaseData, client: Client); + delete(): Promise; + edit(options: EditStageInstanceOptions): Promise; } export class StoreChannel extends GuildChannel { diff --git a/index.js b/index.js index f09f3eb60..fc265b976 100644 --- a/index.js +++ b/index.js @@ -41,6 +41,8 @@ Eris.Role = require("./lib/structures/Role"); Eris.SequentialBucket = require("./lib/util/SequentialBucket"); Eris.Shard = require("./lib/gateway/Shard"); Eris.SharedStream = require("./lib/voice/SharedStream"); +Eris.StageChannel = require("./lib/structures/StageChannel"); +Eris.StageInstance = require("./lib/structures/StageInstance"); Eris.StoreChannel = require("./lib/structures/StoreChannel"); Eris.TextChannel = require("./lib/structures/TextChannel"); Eris.ThreadChannel = require("./lib/structures/ThreadChannel"); diff --git a/lib/Client.js b/lib/Client.js index d327dccf6..af5051177 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -25,6 +25,7 @@ const ThreadMember = require("./structures/ThreadMember"); const UnavailableGuild = require("./structures/UnavailableGuild"); const User = require("./structures/User"); const VoiceConnectionManager = require("./voice/VoiceConnectionManager"); +const StageInstance = require("./structures/StageInstance"); let EventEmitter; try { @@ -704,6 +705,19 @@ class Client extends EventEmitter { }); } + /** + * Create a stage instance + * @arg {String} channelID The ID of the stage channel to create the instance in + * @arg {String} topic The topic for the stage instance + * @returns {Promise} + */ + createStageInstance(channelID, topic) { + return this.requestHandler.request("POST", Endpoints.STAGE_INSTANCES, true, { + channel_id: channelID, + topic: topic + }).then((instance) => new StageInstance(instance, this)); + } + /** * Crosspost (publish) a message to subscribed channels * @arg {String} channelID The ID of the NewsChannel @@ -881,6 +895,15 @@ class Client extends EventEmitter { return this.requestHandler.request("DELETE", Endpoints.USER_BILLING_PREMIUM_SUBSCRIPTION("@me"), true); } + /** + * Delete a stage instance + * @arg {String} channelID The stage channel associated with the instance + * @returns {Promise} + */ + deleteStageInstance(channelID) { + return this.requestHandler.request("DELETE", Endpoints.STAGE_INSTANCE(channelID), true); + } + /** * [USER ACCOUNT] Delete the current user's note for another user * @returns {Promise} @@ -1454,6 +1477,17 @@ class Client extends EventEmitter { }); } + /** + * Update a stage instance + * @arg {String} channelID The ID of the stage channel associated with the instance + * @arg {Object} options The properties to edit + * @arg {String} options.topic The stage instance topic + * @returns {Promise} + */ + editStageInstance(channelID, options) { + return this.requestHandler.request("PATCH", Endpoints.STAGE_INSTANCE(channelID), true, options).then((instance) => new StageInstance(instance, this)); + } + /** * Update the bot's status on all guilds * @arg {String} [status] Sets the bot's status, either "online", "idle", "dnd", or "invisible" @@ -2328,6 +2362,15 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.USER_SETTINGS("@me"), true); } + /** + * Get the stage instance associated with a stage channel + * @arg {String} channelID The stage channel ID + * @returns {Promise} + */ + getStageInstance(channelID) { + return this.requestHandler.request("GET", Endpoints.STAGE_INSTANCE(channelID), true).then((instance) => new StageInstance(instance, this)); + } + /** * Get a list of members that are part of a thread channel * @arg {String} channelID The ID of the thread channel diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 5a93e729a..ad31742cc 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -74,6 +74,8 @@ module.exports.GUILD_VOICE_STATE = (guildID, user) module.exports.GUILDS = "/guilds"; module.exports.INVITE = (inviteID) => `/invites/${inviteID}`; module.exports.OAUTH2_APPLICATION = (appID) => `/oauth2/applications/${appID}`; +module.exports.STAGE_INSTANCE = (channelID) => `/stage-instances/${channelID}`; +module.exports.STAGE_INSTANCES = "/stage-instances"; 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/StageChannel.js b/lib/structures/StageChannel.js index 68df25ebc..933d3872b 100644 --- a/lib/structures/StageChannel.js +++ b/lib/structures/StageChannel.js @@ -15,6 +15,41 @@ class StageChannel extends VoiceChannel { } } + /** + * Create a stage instance + * @arg {String} topic The topic for the stage instance + * @returns {Promise} + */ + createInstance(topic) { + return this.client.createStageInstance.call(this.client, this.id, topic); + } + + /** + * Delete the stage instance for this channel + * @returns {Promise} + */ + deleteInstance() { + return this.client.deleteStageInstance.call(this.client, this.id); + } + + /** + * Update the stage instance for this channel + * @arg {Object} options The properties to edit + * @arg {String} options.topic The stage instance topic + * @returns {Promise} + */ + editInstance(options) { + return this.client.editStageInstance.call(this.client, this.id, options); + } + + /** + * Get the stage instance for this channel + * @returns {Promise} + */ + getInstance() { + return this.client.getStageInstance.call(this.client, this.id); + } + toJSON(props = []) { return super.toJSON([ "topic", diff --git a/lib/structures/StageInstance.js b/lib/structures/StageInstance.js new file mode 100644 index 000000000..03244d12e --- /dev/null +++ b/lib/structures/StageInstance.js @@ -0,0 +1,40 @@ +"use strict"; + +const Base = require("./Base"); + +/** +* Represents a stage instance +* @prop {StageChannel} channel The associated stage channel +* @prop {Guild} guild The guild of the associated stage channel +* @prop {String} id The ID of the stage instance +* @prop {String} topic The stage instance topic +*/ +class StageInstance extends Base { + constructor(data, client) { + super(data.id); + this.client = client; + this.channel = client.getChannel(data.channel_id) || {id: data.channel_id}; + this.guild = client.guilds.get(data.guild_id) || {id: data.guild_id}; + this.topic = data.topic; + } + + /** + * Delete this stage instance + * @returns {Promise} + */ + delete() { + return this.client.deleteStageInstance.call(this.client, this.channel.id); + } + + /** + * Update this stage instance + * @arg {Object} options The properties to edit + * @arg {String} options.topic The stage instance topic + * @returns {Promise} + */ + edit(options) { + return this.client.editStageInstance.call(this.client, options); + } +} + +module.exports = StageInstance; From d1186d4701c737d7c65008e682ede42d8c56700c Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 10:37:01 +0100 Subject: [PATCH 22/90] Expose Message#applicationID --- index.d.ts | 1 + lib/structures/Message.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/index.d.ts b/index.d.ts index 3806d565f..344f12277 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2345,6 +2345,7 @@ declare namespace Eris { export class Message extends Base { activity?: MessageActivity; application?: MessageApplication; + applicationID?: string; attachments: Attachment[]; author: User; channel: T; diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 92e9b7eae..9e200a2d3 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -10,6 +10,7 @@ const User = require("./User"); * Represents a message * @prop {Object?} activity The activity specified in the message * @prop {Object?} application The application of the activity in the message +* @prop {String?} applicationID The ID of the interaction's application * @prop {Array} attachments Array of attachments * @prop {User} author The message author * @prop {PrivateChannel | TextChannel | NewsChannel} channel The channel the message is in. Can be partial with only the id if the channel is not cached. @@ -282,6 +283,9 @@ class Message extends Base { if(data.application !== undefined) { this.application = data.application; } + if(data.application_id !== undefined) { + this.applicationID = data.application_id; + } if(data.reactions) { data.reactions.forEach((reaction) => { From 7b0edbbba79e6d60425045c8258abefa9b71a8d2 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 10:40:50 +0100 Subject: [PATCH 23/90] GuildMember joinedAt can be null --- index.d.ts | 2 +- lib/structures/Member.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 344f12277..71665c1ad 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2319,7 +2319,7 @@ declare namespace Eris { game: Activity | null; guild: Guild; id: string; - joinedAt: number; + joinedAt: number | null; mention: string; nick: string | null; pending?: boolean; diff --git a/lib/structures/Member.js b/lib/structures/Member.js index 8cc7c304e..463ef23d2 100644 --- a/lib/structures/Member.js +++ b/lib/structures/Member.js @@ -24,7 +24,7 @@ const VoiceState = require("./VoiceState"); * @prop {String?} game.url The url of the active game * @prop {Guild} guild The guild the member is in * @prop {String} id The ID of the member -* @prop {Number} joinedAt Timestamp of when the member joined the guild +* @prop {Number?} joinedAt Timestamp of when the member joined the guild * @prop {String} mention A string that mentions the member * @prop {String?} nick The server nickname of the member * @prop {Boolean?} pending Whether the member has passed the guild's Membership Screening requirements @@ -68,7 +68,7 @@ class Member extends Base { this.status = data.status; } if(data.joined_at !== undefined) { - this.joinedAt = Date.parse(data.joined_at); + this.joinedAt = data.joined_at ? Date.parse(data.joined_at) : null; } if(data.client_status !== undefined) { this.clientStatus = Object.assign({web: "offline", desktop: "offline", mobile: "offline"}, data.client_status); From 47f8d20ff27f0b4033dfeb44476d550f424c4488 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 13:37:30 +0100 Subject: [PATCH 24/90] Update Webhook interface (interaction webhooks) --- index.d.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index 71665c1ad..96a5b3888 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1082,13 +1082,18 @@ declare namespace Eris { // Webhook interface Webhook { - avatar?: string; - channel_id: string; - guild_id: string; + application_id: string | null; + avatar: string | null; + channel_id: string | null; + guild_id: string | null; id: string; name: string; - token: string; - user: PartialUser; + source_channel?: { id: string; name: string }; + source_guild: { icon: string | null; id: string; name: string }; + token?: string; + type: 1 | 2 | 3; + url?: string; + user?: PartialUser; } interface WebhookOptions { avatar?: string; From 0de18ca52e731df8238dd201b822b575cb1da8da Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 13:49:25 +0100 Subject: [PATCH 25/90] Fix typo --- lib/gateway/Shard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index fbba69105..73ad30097 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2190,7 +2190,7 @@ class Shard extends EventEmitter { break; } case "THREAD_MEMBERS_UPDATE": { - const channel = this.cleint.getChannel(packet.d.id); + const channel = this.client.getChannel(packet.d.id); if(!channel) { this.emit("debug", `Missing channel ${packet.d.id} in THREAD_MEMBERS_UPDATE`); break; From 239f48db246ded429667235c6e3582b38fbe6580 Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 14:09:54 +0100 Subject: [PATCH 26/90] Stage instance caching and gateway events --- index.d.ts | 6 +++++ lib/gateway/Shard.js | 52 +++++++++++++++++++++++++++++++++++++++++ lib/structures/Guild.js | 10 ++++++++ 3 files changed, 68 insertions(+) diff --git a/index.d.ts b/index.d.ts index 96a5b3888..8e527fb37 100644 --- a/index.d.ts +++ b/index.d.ts @@ -479,6 +479,9 @@ declare namespace Eris { permissions: Permission; position: number; } + interface OldStageInstance { + topic: string; + } interface OldThread { name: string; rateLimitPerUser: number; @@ -553,6 +556,8 @@ declare namespace Eris { event: "relationshipUpdate", listener: (relationship: Relationship, oldRelationship: { type: number }) => void ): T; + (event: "stageInstanceCreate" | "stageInstanceDelete", listener: (stageInstance: StageInstance) => void): T; + (event: "stageInstanceUpdate", listener: (stageInstance: StageInstance, oldStageInstance: OldStageInstance | null) => void): T; (event: "threadCreate" | "threadDelete", listener: (channel: AnyThreadChannel) => void): T; (event: "threadListSync", listener: (guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]) => void): T; (event: "threadMembersUpdate", listener: (channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]) => void): T; @@ -2080,6 +2085,7 @@ declare namespace Eris { shard: Shard; splash: string | null; splashURL: string | null; + stageInstances: Collection; systemChannelFlags: number; systemChannelID: string | null; threads: Collection; diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 73ad30097..46df5579c 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -15,6 +15,7 @@ const User = require("../structures/User"); const Invite = require("../structures/Invite"); const Constants = require("../Constants"); const ThreadChannel = require("../structures/ThreadChannel"); +const StageInstance = require("../structures/StageInstance"); const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSocket") : require("ws"); @@ -2214,6 +2215,57 @@ class Shard extends EventEmitter { this.emit("threadMembersUpdate", channel, removedMembers, addedMembers); break; } + case "STAGE_INSTANCE_CREATE": { + const guild = this.client.guilds.get(packet.d.guild_id); + if(!guild) { + this.emit("debug", `Missing guild ${packet.d.guild_id} in STAGE_INSTANCE_CREATE`); + break; + } + /** + * Fired when a stage instance is created + * @event Client#stageInstanceCreate + * @prop {StageInstance} stageInstance The stage instance + */ + this.emit("stageInstanceCreate", guild.stageInstances.add(packet.d)); + break; + } + case "STAGE_INSTANCE_UPDATE": { + const guild = this.client.guilds.get(packet.d.guild_id); + if(!guild) { + this.emit("stageInstanceUpdate", packet.d, null); + break; + } + const stageInstance = guild.stageInstances.get(packet.d.id); + let oldStageInstance = null; + if(stageInstance) { + oldStageInstance = { + topic: stageInstance.topic + }; + } + /** + * Fired when a stage instance is updated + * @event Client#stageInstanceUpdate + * @prop {StageInstance} stageInstance The stage instance + * @prop {Object?} oldStageInstance The old stage instance. If the stage instance was cached, this will be an object with the properties below. Otherwise, it will be null + * @prop {String} oldStageInstance.topic The stage instance topic + */ + this.emit("stageInstanceUpdate", guild.stageInstances.update(packet.d, this.client), oldStageInstance); + break; + } + case "STAGE_INSTANCE_DELETE": { + const guild = this.client.guilds.get(packet.d.guild_id); + if(!guild) { + this.emit("stageInstanceDelete", new StageInstance(packet.d, this.client)); + break; + } + /** + * Fired when a stage instance is deleted + * @event Client#stageInstanceDelete + * @prop {StageInstance} stageInstance The deleted stage instance + */ + this.emit("stageInstanceDelete", guild.stageInstances.remove(packet.d) || new StageInstance(packet.d, this.client)); + break; + } case "MESSAGE_ACK": // Ignore these case "GUILD_INTEGRATIONS_UPDATE": case "USER_SETTINGS_UPDATE": diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index bdeb56bc2..ebfb92ff9 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -10,6 +10,7 @@ const Role = require("./Role"); const VoiceState = require("./VoiceState"); const Permission = require("./Permission"); const {Permissions} = require("../Constants"); +const StageInstance = require("./StageInstance"); const ThreadChannel = require("./ThreadChannel"); /** @@ -61,6 +62,7 @@ const ThreadChannel = require("./ThreadChannel"); * @prop {Shard} shard The Shard that owns the guild * @prop {String?} splash The hash of the guild splash image, or null if no splash (VIP only) * @prop {String?} splashURL The URL of the guild's splash image +* @prop {Collection} stageInstances Collection of stage instances in the guild * @prop {Number} systemChannelFlags The flags for the system channel * @prop {String?} systemChannelID The ID of the default channel for system messages (built-in join messages and boost messages) * @prop {Collection} threads Collection of threads that the current user has permission to view @@ -85,6 +87,7 @@ class Guild extends Base { this.channels = new Collection(GuildChannel); this.threads = new Collection(ThreadChannel); this.members = new Collection(Member); + this.stageInstances = new Collection(StageInstance); this.memberCount = data.member_count; this.roles = new Collection(Role); this.applicationID = data.application_id; @@ -152,6 +155,13 @@ class Guild extends Base { member.id = member.user.id; this.members.add(member, this); } + } + + if(data.stage_instances) { + for(const stageInstance of data.stage_instances) { + stageInstance.guild_id = this.id; + this.stageInstances.add(stageInstance, client); + } } if(data.presences) { From b6f93de3439a926ce0b38da41141bf7e0b5176df Mon Sep 17 00:00:00 2001 From: Bsian Date: Thu, 27 May 2021 16:55:51 +0100 Subject: [PATCH 27/90] Stage discovery --- index.d.ts | 21 ++++++++++++++------- lib/Client.js | 12 ++++++++---- lib/gateway/Shard.js | 4 ++++ lib/structures/StageChannel.js | 11 +++++++---- lib/structures/StageInstance.js | 21 ++++++++++++++++++--- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/index.d.ts b/index.d.ts index 8e527fb37..3d42ec399 100644 --- a/index.d.ts +++ b/index.d.ts @@ -71,6 +71,7 @@ declare namespace Eris { // Voice type ConverterCommand = "./ffmpeg" | "./avconv" | "ffmpeg" | "avconv"; + type StageInstancePrivacyLevel = 1 | 2; // Webhook type MessageWebhookContent = Pick; @@ -480,6 +481,8 @@ declare namespace Eris { position: number; } interface OldStageInstance { + discoverableDisabled: boolean; + privacyLevel: StageInstancePrivacyLevel; topic: string; } interface OldThread { @@ -1040,8 +1043,9 @@ declare namespace Eris { } // Voice - interface EditStageInstanceOptions { - topic: string; + interface StageInstanceOptions { + privacyLevel?: StageInstancePrivacyLevel; + topic?: string; } interface UncachedMemberVoiceState { id: string; @@ -1699,7 +1703,7 @@ declare namespace Eris { createPrivateThread(channelID: string, options: CreateThreadOptions): Promise; createPublicThread(channelID: string, options: CreateThreadOptions): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; - createStageInstance(channelID: string, topic: string): Promise; + createStageInstance(channelID: string, options: StageInstanceOptions): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -1761,7 +1765,7 @@ declare namespace Eris { data: { friendSync: boolean; visibility: number } ): Promise; editSelfSettings(data: UserSettings): Promise; - editStageInstance(channelID: string, options: EditStageInstanceOptions): Promise; + editStageInstance(channelID: string, options: StageInstanceOptions): Promise; editStatus(status: Status, activities?: ActivityPartial[] | ActivityPartial): void; editStatus(activities?: ActivityPartial[] | ActivityPartial): void; editUserNote(userID: string, note: string): Promise; @@ -2649,20 +2653,23 @@ declare namespace Eris { export class StageChannel extends VoiceChannel { topic?: string; type: 13; - createInstance(topic: string): Promise; + createInstance(options: StageInstanceOptions): Promise; deleteInstance(): Promise; - editInstance(options: EditStageInstanceOptions): Promise; + editInstance(options: StageInstanceOptions): Promise; getInstance(): Promise; } export class StageInstance extends Base { client: Client; channel: StageChannel | Uncached; + discoverableDisabled: boolean; guild: Guild | Uncached; + privacyLevel: StageInstancePrivacyLevel; topic: string; constructor(data: BaseData, client: Client); + update(data: BaseData): void; delete(): Promise; - edit(options: EditStageInstanceOptions): Promise; + edit(options: StageInstanceOptions): Promise; } export class StoreChannel extends GuildChannel { diff --git a/lib/Client.js b/lib/Client.js index af5051177..775d837d0 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -708,13 +708,16 @@ class Client extends EventEmitter { /** * Create a stage instance * @arg {String} channelID The ID of the stage channel to create the instance in - * @arg {String} topic The topic for the stage instance + * @arg {Object} options The stage instance options + * @arg {Number} [options.privacyLevel] The privacy level of the stage instance. 1 is public, 2 is guild only + * @arg {String} options.topic The stage instance topic * @returns {Promise} */ - createStageInstance(channelID, topic) { + createStageInstance(channelID, options) { return this.requestHandler.request("POST", Endpoints.STAGE_INSTANCES, true, { channel_id: channelID, - topic: topic + privacy_level: options.privacyLevel, + topic: options.topic }).then((instance) => new StageInstance(instance, this)); } @@ -1481,7 +1484,8 @@ class Client extends EventEmitter { * Update a stage instance * @arg {String} channelID The ID of the stage channel associated with the instance * @arg {Object} options The properties to edit - * @arg {String} options.topic The stage instance topic + * @arg {Number} [options.privacyLevel] The privacy level of the stage instance. 1 is public, 2 is guild only + * @arg {String} [options.topic] The stage instance topic * @returns {Promise} */ editStageInstance(channelID, options) { diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 46df5579c..495194071 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2239,6 +2239,8 @@ class Shard extends EventEmitter { let oldStageInstance = null; if(stageInstance) { oldStageInstance = { + discoverableDisabled: stageInstance.discoverableDisabled, + privacyLevel: stageInstance.privacyLevel, topic: stageInstance.topic }; } @@ -2247,6 +2249,8 @@ class Shard extends EventEmitter { * @event Client#stageInstanceUpdate * @prop {StageInstance} stageInstance The stage instance * @prop {Object?} oldStageInstance The old stage instance. If the stage instance was cached, this will be an object with the properties below. Otherwise, it will be null + * @prop {Boolean} oldStageInstance.discoverableDisabled Whether or not stage discovery was disabled + * @prop {Number} oldStageInstance.privacyLevel The privacy level of the stage instance. 1 is public, 2 is guild only * @prop {String} oldStageInstance.topic The stage instance topic */ this.emit("stageInstanceUpdate", guild.stageInstances.update(packet.d, this.client), oldStageInstance); diff --git a/lib/structures/StageChannel.js b/lib/structures/StageChannel.js index 933d3872b..25b5e9d66 100644 --- a/lib/structures/StageChannel.js +++ b/lib/structures/StageChannel.js @@ -17,11 +17,13 @@ class StageChannel extends VoiceChannel { /** * Create a stage instance - * @arg {String} topic The topic for the stage instance + * @arg {Object} options The stage instance options + * @arg {Number} [options.privacyLevel] The privacy level of the stage instance. 1 is public, 2 is guild only + * @arg {String} options.topic The stage instance topic * @returns {Promise} */ - createInstance(topic) { - return this.client.createStageInstance.call(this.client, this.id, topic); + createInstance(options) { + return this.client.createStageInstance.call(this.client, this.id, options); } /** @@ -35,7 +37,8 @@ class StageChannel extends VoiceChannel { /** * Update the stage instance for this channel * @arg {Object} options The properties to edit - * @arg {String} options.topic The stage instance topic + * @arg {Number} [options.privacyLevel] The privacy level of the stage instance. 1 is public, 2 is guild only + * @arg {String} [options.topic] The stage instance topic * @returns {Promise} */ editInstance(options) { diff --git a/lib/structures/StageInstance.js b/lib/structures/StageInstance.js index 03244d12e..be99bdfb2 100644 --- a/lib/structures/StageInstance.js +++ b/lib/structures/StageInstance.js @@ -5,8 +5,10 @@ const Base = require("./Base"); /** * Represents a stage instance * @prop {StageChannel} channel The associated stage channel +* @prop {Boolean} discoverableDisabled Whether or not stage discovery is disabled * @prop {Guild} guild The guild of the associated stage channel * @prop {String} id The ID of the stage instance +* @prop {Number} privacyLevel The privacy level of the stage instance. 1 is public, 2 is guild only * @prop {String} topic The stage instance topic */ class StageInstance extends Base { @@ -15,7 +17,19 @@ class StageInstance extends Base { this.client = client; this.channel = client.getChannel(data.channel_id) || {id: data.channel_id}; this.guild = client.guilds.get(data.guild_id) || {id: data.guild_id}; - this.topic = data.topic; + this.update(data); + } + + update(data) { + if(data.discoverable_disabled !== undefined) { + this.discoverableDisabled = data.discoverable_disabled; + } + if(data.privacy_level !== undefined) { + this.privacyLevel = data.privacy_level; + } + if(data.topic !== undefined) { + this.topic = data.topic; + } } /** @@ -29,11 +43,12 @@ class StageInstance extends Base { /** * Update this stage instance * @arg {Object} options The properties to edit - * @arg {String} options.topic The stage instance topic + * @arg {Number} [options.privacyLevel] The privacy level of the stage instance. 1 is public, 2 is guild only + * @arg {String} [options.topic] The stage instance topic * @returns {Promise} */ edit(options) { - return this.client.editStageInstance.call(this.client, options); + return this.client.editStageInstance.call(this.client, this.channel.id, options); } } From 6fcfcc703b1ae7f79cd2fa4ca093f4396438038b Mon Sep 17 00:00:00 2001 From: Bsian Date: Sat, 5 Jun 2021 01:02:45 +0100 Subject: [PATCH 28/90] Remove Guild#region --- index.d.ts | 4 ---- lib/Client.js | 4 ---- lib/gateway/Shard.js | 2 -- lib/structures/Guild.js | 6 ------ 4 files changed, 16 deletions(-) diff --git a/index.d.ts b/index.d.ts index 3d42ec399..a2dd56a46 100644 --- a/index.d.ts +++ b/index.d.ts @@ -419,7 +419,6 @@ declare namespace Eris { premiumSubscriptionCount?: number; premiumTier: PremiumTier; publicUpdatesChannelID: string | null; - region: string; rulesChannelID: string | null; splash: string | null; systemChannelFlags: number; @@ -647,7 +646,6 @@ declare namespace Eris { defaultNotifications?: DefaultNotifications; explicitContentFilter?: ExplicitContentFilter; icon?: string; - region?: string; roles?: PartialRole[]; systemChannelID: string; verificationLevel?: VerificationLevel; @@ -716,7 +714,6 @@ declare namespace Eris { ownerID?: string; preferredLocale?: string; publicUpdatesChannelID?: string; - region?: string; rulesChannelID?: string; splash?: string; systemChannelFlags?: number; @@ -2083,7 +2080,6 @@ declare namespace Eris { primaryCategory?: DiscoveryCategory; primaryCategoryID?: number; publicUpdatesChannelID: string; - region: string; roles: Collection; rulesChannelID: string | null; shard: Shard; diff --git a/lib/Client.js b/lib/Client.js index 775d837d0..694a5eb1f 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -504,7 +504,6 @@ class Client extends EventEmitter { * @arg {Number} [options.defaultNotifications] The default notification settings for the guild. 0 is "All Messages", 1 is "Only @mentions". * @arg {Number} [options.explicitContentFilter] The level of the explicit content filter for messages/images in the guild. 0 disables message scanning, 1 enables scanning the messages of members without roles, 2 enables scanning for all messages. * @arg {String} [options.icon] The guild icon as a base64 data URI. Note: base64 strings alone are not base64 data URI strings - * @arg {String} [options.region] The region of the guild * @arg {Array} [options.roles] The new roles of the guild, the first one is the @everyone role. IDs are placeholders which allow channel overwrites. * @arg {String} [options.systemChannelID] The ID of the system channel * @arg {Number} [options.verificationLevel] The guild verification level @@ -517,7 +516,6 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.GUILDS, true, { name: name, - region: options.region, icon: options.icon, verification_level: options.verificationLevel, default_message_notifications: options.defaultNotifications, @@ -1098,7 +1096,6 @@ class Client extends EventEmitter { * @arg {String} [options.ownerID] The ID of the user to transfer server ownership to (bot user must be owner) * @arg {String} [options.preferredLocale] Preferred "COMMUNITY" guild language used in server discovery and notices from Discord * @arg {String} [options.publicUpdatesChannelID] The id of the channel where admins and moderators of "COMMUNITY" guilds receive notices from Discord - * @arg {String} [options.region] The region of the guild * @arg {String} [options.rulesChannelID] The id of the channel where "COMMUNITY" guilds display rules and/or guidelines * @arg {String} [options.splash] The guild splash image as a base64 data URI (VIP only). Note: base64 strings alone are not base64 data URI strings * @arg {Number} [options.systemChannelFlags] The flags for the system channel @@ -1110,7 +1107,6 @@ class Client extends EventEmitter { editGuild(guildID, options, reason) { return this.requestHandler.request("PATCH", Endpoints.GUILD(guildID), true, { name: options.name, - region: options.region, icon: options.icon, verification_level: options.verificationLevel, default_message_notifications: options.defaultNotifications, diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 495194071..f0f788f8e 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -1246,7 +1246,6 @@ class Shard extends EventEmitter { premiumSubscriptionCount: guild.premiumSubscriptionCount, premiumTier: guild.premiumTier, publicUpdatesChannelID: guild.publicUpdatesChannelID, - region: guild.region, rulesChannelID: guild.rulesChannelID, splash: guild.splash, systemChannelFlags: guild.systemChannelFlags, @@ -1280,7 +1279,6 @@ class Shard extends EventEmitter { * @prop {Number?} oldGuild.premiumSubscriptionCount The total number of users currently boosting this guild * @prop {Number} oldGuild.premiumTier Nitro boost level of the guild * @prop {String?} oldGuild.publicUpdatesChannelID ID of the guild's updates channel if the guild has "COMMUNITY" features - * @prop {String} oldGuild.region The region of the guild * @prop {String?} oldGuild.rulesChannelID The channel where "COMMUNITY" guilds display rules and/or guidelines * @prop {String?} oldGuild.splash The hash of the guild splash image, or null if no splash (VIP only) * @prop {Number} oldGuild.systemChannelFlags the flags for the system channel diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index ebfb92ff9..9962ab6cc 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -56,7 +56,6 @@ const ThreadChannel = require("./ThreadChannel"); * @prop {Object?} primaryCategory The guild's primary discovery category * @prop {Number?} primaryCategoryID The guild's primary discovery category ID * @prop {String?} publicUpdatesChannelID ID of the guild's updates channel if the guild has "COMMUNITY" features -* @prop {String} region The region of the guild * @prop {Collection} roles Collection of Roles in the guild * @prop {String?} rulesChannelID The channel where "COMMUNITY" guilds display rules and/or guidelines * @prop {Shard} shard The Shard that owns the guild @@ -223,9 +222,6 @@ class Guild extends Base { if(data.banner !== undefined) { this.banner = data.banner; } - if(data.region !== undefined) { - this.region = data.region; - } if(data.owner_id !== undefined) { this.ownerID = data.owner_id; } @@ -526,7 +522,6 @@ class Guild extends Base { * @arg {String} [options.ownerID] The ID of the member to transfer guild ownership to (bot user must be owner) * @arg {String} [options.preferredLocale] Preferred "COMMUNITY" guild language used in server discovery and notices from Discord * @arg {String} [options.publicUpdatesChannelID] The id of the channel where admins and moderators of "COMMUNITY" guilds receive notices from Discord - * @arg {String} [options.region] The region of the guild * @arg {String} [options.rulesChannelID] The id of the channel where "COMMUNITY" guilds display rules and/or guidelines * @arg {String} [options.splash] The guild splash image as a base64 data URI (VIP only). Note: base64 strings alone are not base64 data URI strings * @arg {Number} [options.systemChannelFlags] The flags for the system channel @@ -1047,7 +1042,6 @@ class Guild extends Base { "primaryCategory", "primaryCategoryID", "publicUpdatesChannelID", - "region", "roles", "rulesChannelID", "splash", From a3b0fea7a032dce83cf8335ec37ff313739d726f Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 23 Jun 2021 22:05:36 +0100 Subject: [PATCH 29/90] Add channel types to thread channel classes --- index.d.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index a2dd56a46..74e586b57 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2427,7 +2427,9 @@ declare namespace Eris { getPins(): Promise[]>; } - export class NewsThreadChannel extends ThreadChannel {} + export class NewsThreadChannel extends ThreadChannel { + type: 10; + } export class Permission extends Base { allow: bigint; @@ -2492,9 +2494,13 @@ declare namespace Eris { unsendMessage(messageID: string): Promise; } - export class PrivateThreadChannel extends ThreadChannel {} + export class PrivateThreadChannel extends ThreadChannel { + type: 12; + } - export class PublicThreadChannel extends ThreadChannel {} + export class PublicThreadChannel extends ThreadChannel { + type: 11; + } export class Relationship extends Base implements Omit { activities: Activity[] | null; @@ -2729,6 +2735,7 @@ declare namespace Eris { ownerID: string; rateLimitPerUser: number; threadMetadata: ThreadMetadata; + type: 10 | 11 | 12; constructor(data: BaseData, client: Client, messageLimit?: number); getMembers(): Promise; join(userID?: string): Promise; From 6709477e1e1586a59cb27056503775786faa7374 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 23 Jun 2021 22:33:49 +0100 Subject: [PATCH 30/90] Update method names and docs to match Discord --- index.d.ts | 14 +++---- lib/Client.js | 74 +++++++++++++++++------------------ lib/rest/Endpoints.js | 14 +++---- lib/structures/Message.js | 6 +-- lib/structures/TextChannel.js | 18 ++++----- 5 files changed, 63 insertions(+), 63 deletions(-) diff --git a/index.d.ts b/index.d.ts index 74e586b57..b419c58a0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1697,10 +1697,10 @@ declare namespace Eris { createGuildFromTemplate(code: string, name: string, icon?: string): Promise; createGuildTemplate(guildID: string, name: string, description?: string | null): Promise; createMessage(channelID: string, content: MessageContent, file?: MessageFile | MessageFile[]): Promise; - createPrivateThread(channelID: string, options: CreateThreadOptions): Promise; - createPublicThread(channelID: string, options: CreateThreadOptions): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; createStageInstance(channelID: string, options: StageInstanceOptions): Promise; + createThreadWithMessage(channelID: string, options: CreateThreadOptions): Promise; + createThreadWithoutMessage(channelID: string, options: CreateThreadOptions): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -2393,7 +2393,7 @@ declare namespace Eris { addReaction(reaction: string): Promise; /** @deprecated */ addReaction(reaction: string, userID: string): Promise; - createPublicThread(options: CreateThreadOptions): Promise; + createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; crosspost(): Promise : never>; delete(reason?: string): Promise; deleteWebhook(token: string): Promise; @@ -2415,7 +2415,7 @@ declare namespace Eris { type: 5; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; - createPublicThread(messageID: string, options: CreateThreadOptions): Promise; + createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; crosspostMessage(messageID: string): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; follow(webhookChannelID: string): Promise; @@ -2499,7 +2499,7 @@ declare namespace Eris { } export class PublicThreadChannel extends ThreadChannel { - type: 11; + type: 10 | 11; } export class Relationship extends Base implements Omit { @@ -2692,8 +2692,8 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; - createPrivateThread(options: CreateThreadOptions): Promise; - createPublicThread(messageID: string, options: CreateThreadOptions): Promise; + createThreadWithoutMessage(options: CreateThreadOptions): Promise; + createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; createWebhook(options: { name: string; avatar?: string | null}, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; diff --git a/lib/Client.js b/lib/Client.js index 694a5eb1f..86c4f2562 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -639,37 +639,6 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.CHANNEL_MESSAGES(channelID), true, content, file).then((message) => new Message(message, this)); } - /** - * Create a private thread - * @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 {String} options.name The thread channel name - * @returns {Promise} - */ - createPrivateThread(channelID, options) { - return this.requestHandler.request("POST", Endpoints.GUILD_THREAD_PRIVATE(channelID), true, { - name: options.name, - auto_archive_duration: options.autoArchiveDuration - }).then((channel) => Channel.from(channel, this)); - } - - /** - * Create a public thread - * @arg {String} channelID The ID of the channel - * @arg {String} messageID The ID of the message to create the thread from - * @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 {String} options.name The thread channel name - * @returns {Promise} - */ - createPublicThread(channelID, messageID, options) { - return this.requestHandler.request("POST", Endpoints.GUILD_THREAD_PUBLIC(channelID, messageID), true, { - name: options.name, - auto_archive_duration: options.autoArchiveDuration - }).then((channel) => Channel.from(channel, this)); - } - /** * Create a guild role * @arg {String} guildID The ID of the guild to create the role in @@ -719,6 +688,37 @@ class Client extends EventEmitter { }).then((instance) => new StageInstance(instance, this)); } + /** + * Create a thread with an existing message + * @arg {String} channelID The ID of the channel + * @arg {String} messageID The ID of the message to create the thread from + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createThreadWithMessage(channelID, messageID, options) { + return this.requestHandler.request("POST", Endpoints.THREAD_WITH_MESSAGE(channelID, messageID), true, { + name: options.name, + auto_archive_duration: options.autoArchiveDuration + }).then((channel) => Channel.from(channel, this)); + } + + /** + * Create a thread without an existing message + * @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 {String} options.name The thread channel name + * @returns {Promise} + */ + createThreadWithoutMessage(channelID, options) { + return this.requestHandler.request("POST", Endpoints.THREAD_WITHOUT_MESSAGE(channelID), true, { + name: options.name, + auto_archive_duration: options.autoArchiveDuration + }).then((channel) => Channel.from(channel, this)); + } + /** * Crosspost (publish) a message to subscribed channels * @arg {String} channelID The ID of the NewsChannel @@ -1685,7 +1685,7 @@ class Client extends EventEmitter { * @returns {Promise>} */ getActiveThreads(channelID) { - return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ACTIVE(channelID), true).then((channels) => channels.map((c) => Channel.from(c, this))); + return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((channels) => channels.map((c) => Channel.from(c, this))); } /** @@ -1698,7 +1698,7 @@ class Client extends EventEmitter { * @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 */ getArchivedThreads(channelID, type, options = {}) { - return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED(channelID, type), true, options).then((response) => { + return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED(channelID, type), true, options).then((response) => { return { hasMore: response.has_more, members: response.members.map((member) => new ThreadMember(member, this)), @@ -2001,7 +2001,7 @@ class Client extends EventEmitter { * @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 */ getJoinedPrivateArchivedThreads(channelID, options = {}) { - return this.requestHandler.request("GET", Endpoints.GUILD_THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { + return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { return { hasMore: response.has_more, members: response.members.map((member) => new ThreadMember(member, this)), @@ -2377,7 +2377,7 @@ class Client extends EventEmitter { * @returns {Promise>} */ getThreadMembers(channelID) { - return this.requestHandler.request("GET", Endpoints.GUILD_THREAD_MEMBERS(channelID), true).then((members) => members.map((member) => new ThreadMember(member, this))); + return this.requestHandler.request("GET", Endpoints.THREAD_MEMBERS(channelID), true).then((members) => members.map((member) => new ThreadMember(member, this))); } /** @@ -2426,7 +2426,7 @@ class Client extends EventEmitter { * @returns {Promise} */ joinThread(channelID, userID = "@me") { - return this.requestHandler.request("PUT", Endpoints.GUILD_THREAD_MEMBER(channelID, userID), true); + return this.requestHandler.request("PUT", Endpoints.THREAD_MEMBER(channelID, userID), true); } /** @@ -2486,7 +2486,7 @@ class Client extends EventEmitter { * @returns {Promise} */ leaveThread(channelID, userID = "@me") { - return this.requestHandler.request("DELETE", Endpoints.GUILD_THREAD_MEMBER(channelID, userID), true); + return this.requestHandler.request("DELETE", Endpoints.THREAD_MEMBER(channelID, userID), true); } /** diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index ad31742cc..16fdf97a8 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -58,13 +58,6 @@ module.exports.GUILD_ROLES = (guildID) module.exports.GUILD_TEMPLATE = (code) => `/guilds/templates/${code}`; module.exports.GUILD_TEMPLATES = (guildID) => `/guilds/${guildID}/templates`; module.exports.GUILD_TEMPLATE_GUILD = (guildID, code) => `/guilds/${guildID}/templates/${code}`; -module.exports.GUILD_THREAD_MEMBER = (channelID, userID) => `/channels/${channelID}/thread-members/${userID}`; -module.exports.GUILD_THREAD_MEMBERS = (channelID) => `/channels/${channelID}/thread-members`; -module.exports.GUILD_THREAD_PRIVATE = (channelID) => `/channels/${channelID}/threads`; -module.exports.GUILD_THREAD_PUBLIC = (channelID, msgID) => `/channels/${channelID}/messages/${msgID}/threads`; -module.exports.GUILD_THREADS_ACTIVE = (channelID) => `/channels/${channelID}/threads/active`; -module.exports.GUILD_THREADS_ARCHIVED = (channelID, type) => `/channels/${channelID}/threads/archived/${type}`; -module.exports.GUILD_THREADS_ARCHIVED_JOINED = (channelID) => `/channels/${channelID}/users/@me/threads/archived/private`; module.exports.GUILD_VOICE_REGIONS = (guildID) => `/guilds/${guildID}/regions`; module.exports.GUILD_WEBHOOKS = (guildID) => `/guilds/${guildID}/webhooks`; module.exports.GUILD_WELCOME_SCREEN = (guildID) => `/guilds/${guildID}/welcome-screen`; @@ -76,6 +69,13 @@ module.exports.INVITE = (inviteID) module.exports.OAUTH2_APPLICATION = (appID) => `/oauth2/applications/${appID}`; module.exports.STAGE_INSTANCE = (channelID) => `/stage-instances/${channelID}`; module.exports.STAGE_INSTANCES = "/stage-instances"; +module.exports.THREAD_MEMBER = (channelID, userID) => `/channels/${channelID}/thread-members/${userID}`; +module.exports.THREAD_MEMBERS = (channelID) => `/channels/${channelID}/thread-members`; +module.exports.THREAD_WITH_MESSAGE = (channelID, msgID) => `/channels/${channelID}/messages/${msgID}/threads`; +module.exports.THREAD_WITHOUT_MESSAGE = (channelID) => `/channels/${channelID}/threads`; +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.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/Message.js b/lib/structures/Message.js index 9e200a2d3..a19bde178 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -376,14 +376,14 @@ class Message extends Base { } /** - * Create a public thread from this message + * Create a thread with this 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 {String} options.name The thread channel name * @returns {Promise} */ - createPublicThread(options) { - return this.client.createPublicThread.call(this.client, this.channel.id, this.id, options); + createThreadWithMessage(options) { + return this.client.createThreadWithMessage.call(this.client, this.channel.id, this.id, options); } /** diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 9cf42f12f..a4b58467d 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -87,26 +87,26 @@ class TextChannel extends GuildChannel { } /** - * Create a private thread + * Create a thread with an existing message + * @arg {String} messageID The ID of the message to create the thread from * @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 {String} options.name The thread channel name - * @returns {Promise} + * @returns {Promise} */ - createPrivateThread(options) { - return this.client.createPrivateThread.call(this.client, this.id, options); + createThreadWithMessage(messageID, options) { + return this.client.createThreadWithMessage.call(this.client, this.id, messageID, options); } /** - * Create a public thread - * @arg {String} messageID The ID of the message to create the thread from + * 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 {String} options.name The thread channel name - * @returns {Promise} + * @returns {Promise} */ - createPublicThread(messageID, options) { - return this.client.createPublicThread.call(this.client, this.id, messageID, options); + createThreadWithoutMessage(options) { + return this.client.createThreadWithoutMessage.call(this.client, this.id, options); } /** From 289b83478dc6f4006516be5fce2aead20eca6c44 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 23 Jun 2021 22:40:00 +0100 Subject: [PATCH 31/90] Thread without message type --- index.d.ts | 7 +++++-- lib/Client.js | 1 + lib/structures/TextChannel.js | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index b419c58a0..1357f3b55 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1027,6 +1027,9 @@ declare namespace Eris { autoArchiveDuration: number; name: string; } + interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { + type: AnyThreadChannel["type"]; + } interface GetArchivedThreadsOptions { before?: Date; limit?: number; @@ -1700,7 +1703,7 @@ declare namespace Eris { createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; createStageInstance(channelID: string, options: StageInstanceOptions): Promise; createThreadWithMessage(channelID: string, options: CreateThreadOptions): Promise; - createThreadWithoutMessage(channelID: string, options: CreateThreadOptions): Promise; + createThreadWithoutMessage(channelID: string, options: CreateThreadWithoutMessageOptions): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -2692,7 +2695,7 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; - createThreadWithoutMessage(options: CreateThreadOptions): Promise; + createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions): Promise; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; createWebhook(options: { name: string; avatar?: string | null}, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; diff --git a/lib/Client.js b/lib/Client.js index 86c4f2562..158098a40 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -710,6 +710,7 @@ class Client extends EventEmitter { * @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 {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 * @returns {Promise} */ createThreadWithoutMessage(channelID, options) { diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index a4b58467d..f7eef8e1a 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -103,6 +103,7 @@ class TextChannel extends GuildChannel { * @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 {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 * @returns {Promise} */ createThreadWithoutMessage(options) { From 5184e454ee2cbbc7a839cc2a9e436455ced048b7 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 22:55:02 +0100 Subject: [PATCH 32/90] Correct documentation --- lib/structures/PrivateChannel.js | 1 - lib/structures/TextChannel.js | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/structures/PrivateChannel.js b/lib/structures/PrivateChannel.js index 7555f45b4..67cf5618d 100644 --- a/lib/structures/PrivateChannel.js +++ b/lib/structures/PrivateChannel.js @@ -39,7 +39,6 @@ class PrivateChannel extends Channel { /** * Create a message in a text channel - * Note: If you want to DM someone, the user ID is **not** the DM channel ID. use Client.getDMChannel() to get the DM channel ID for a user * @arg {String | Object} content A string or object. If an object is passed: * @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default) * @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here. diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index f7eef8e1a..e2086efb6 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -60,7 +60,6 @@ class TextChannel extends GuildChannel { /** * Create a message in the channel - * Note: If you want to DM someone, the user ID is **not** the DM channel ID. use Client.getDMChannel() to get the DM channel ID for a user * @arg {String | Object} content A string or object. If an object is passed: * @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default) * @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here. From b405958adfd8c66e982d42fc727dc608fa7b6f0d Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 23:35:39 +0100 Subject: [PATCH 33/90] Add missing functionality methods --- index.d.ts | 36 ++++++ lib/structures/ThreadChannel.js | 198 ++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/index.d.ts b/index.d.ts index 1357f3b55..7223acb4d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -69,6 +69,9 @@ declare namespace Eris { type FriendSuggestionReasons = { name: string; platform_type: string; type: number }[]; type Status = "online" | "idle" | "dnd" | "offline"; + // Thread + type EditThreadOptions = Pick; + // Voice type ConverterCommand = "./ffmpeg" | "./avconv" | "ffmpeg" | "avconv"; type StageInstancePrivacyLevel = 1 | 2; @@ -2432,6 +2435,11 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; + createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; } export class Permission extends Base { @@ -2499,10 +2507,20 @@ declare namespace Eris { export class PrivateThreadChannel extends ThreadChannel { type: 12; + createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): 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: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; } export class Relationship extends Base implements Omit { @@ -2740,9 +2758,27 @@ declare namespace Eris { threadMetadata: ThreadMetadata; type: 10 | 11 | 12; constructor(data: BaseData, client: Client, messageLimit?: number); + addMessageReaction(messageID: string, reaction: string): Promise; + createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + deleteMessage(messageID: string, reason?: string): Promise; + deleteMessages(messageIDs: string[], reason?: string): Promise; + edit(options: Pick, reason?: string): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise>; getMembers(): Promise; + getMessage(messageID: string): Promise>; + getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; join(userID?: string): Promise; leave(userID?: string): Promise; + pinMessage(messageID: string): Promise; + purge(options: PurgeChannelOptions): Promise; + removeMessageReaction(messageID: string, reaction: string, userID?: string): Promise; + removeMessageReactionEmoji(messageID: string, reaction: string): Promise; + removeMessageReactions(messageID: string): Promise; + sendTyping(): Promise; + unpinMessage(messageID: string): Promise; + unsendMessage(messageID: string): Promise; } export class ThreadMember extends Base { diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 201563961..6201380ff 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -62,6 +62,82 @@ class ThreadChannel extends GuildChannel { } } + /** + * Add a reaction to a message + * @arg {String} messageID The ID of the message + * @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji) + * @returns {Promise} + */ + addMessageReaction(messageID, reaction) { + return this.client.addMessageReaction.call(this.client, this.id, messageID, reaction); + } + + /** + * Create a message in the channel + * @arg {String | Object} content A string or object. If an object is passed: + * @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default) + * @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here. + * @arg {Boolean | Array} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow. + * @arg {Boolean | Array} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow. + * @arg {Boolean} [options.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to + * @arg {String} content.content A content string + * @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 + * @arg {Object} [content.messageReference] The message reference, used when replying to messages + * @arg {String} [content.messageReference.channelID] The channel ID of the referenced message + * @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message + * @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 {Boolean} [content.tts] Set the message TTS flag + * @arg {Object} [file] A file object + * @arg {Buffer} file.file A buffer containing file data + * @arg {String} file.name What to name the file + * @returns {Promise} + */ + createMessage(content, file) { + return this.client.createMessage.call(this.client, this.id, content, file); + } + + /** + * Delete a message + * @arg {String} messageID The ID of the message + * @arg {String} [reason] The reason to be displayed in audit logs + * @returns {Promise} + */ + deleteMessage(messageID, reason) { + return this.client.deleteMessage.call(this.client, this.id, messageID, reason); + } + + /** + * Bulk delete messages (bot accounts only) + * @arg {Array} messageIDs Array of message IDs to delete + * @arg {String} [reason] The reason to be displayed in audit logs + * @returns {Promise} + */ + deleteMessages(messageIDs, reason) { + return this.client.deleteMessages.call(this.client, this.id, messageIDs, reason); + } + + /** + * Edit a message + * @arg {String} messageID The ID of the message + * @arg {String | Array | Object} content A string, array of strings, or object. If an object is passed: + * @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default) + * @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here. + * @arg {Boolean | Array} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow. + * @arg {Boolean | Array} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow. + * @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 + * @arg {Object | Array} [content.file] A file object (or an Array of them) + * @arg {Buffer} content.file[].file A buffer containing file data + * @arg {String} content.file[].name What to name the file + * @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference + * @returns {Promise} + */ + editMessage(messageID, content) { + return this.client.editMessage.call(this.client, this.id, messageID, content); + } /** * Get a list of members that are part of this thread channel @@ -71,6 +147,49 @@ class ThreadChannel extends GuildChannel { return this.client.getThreadMembers.call(this.client, this.id); } + /** + * Get a previous message in the channel + * @arg {String} messageID The ID of the message + * @returns {Promise} + */ + getMessage(messageID) { + return this.client.getMessage.call(this.client, this.id, messageID); + } + + /** + * Get a list of users who reacted with a specific reaction + * @arg {String} messageID The ID of the message + * @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji) + * @arg {Object} [options] Options for the request. + * @arg {Number} [options.limit=100] The maximum number of users to get + * @arg {String} [options.after] Get users after this user ID + * @returns {Promise>} + */ + getMessageReaction(messageID, reaction, options) { + return this.client.getMessageReaction.call(this.client, this.id, messageID, reaction, options); + } + + /** + * Get previous messages in the channel + * @arg {Object} [options] Options for the request + * @arg {String} [options.after] Get messages after this message ID + * @arg {String} [options.around] Get messages around this message ID (does not work with limit > 100) + * @arg {String} [options.before] Get messages before this message ID + * @arg {Number} [options.limit=50] The max number of messages to get + * @returns {Promise>} + */ + getMessages(options) { + return this.client.getMessages.call(this.client, this.id, options); + } + + /** + * Get all the pins in the channel + * @returns {Promise>} + */ + getPins() { + return this.client.getPins.call(this.client, this.id); + } + /** * Join a thread * @arg {String} [userID="@me"] The user ID of the user joining @@ -89,6 +208,85 @@ class ThreadChannel extends GuildChannel { return this.client.leaveThread.call(this.client, this.id, userID); } + /** + * Pin a message + * @arg {String} messageID The ID of the message + * @returns {Promise} + */ + pinMessage(messageID) { + return this.client.pinMessage.call(this.client, this.id, messageID); + } + + /** + * Purge previous messages in the channel with an optional filter (bot accounts only) + * @arg {Object} options Options for the request. If this is a number + * @arg {String} [options.after] Get messages after this message ID + * @arg {String} [options.before] Get messages before this message ID + * @arg {Function} [options.filter] Optional filter function that returns a boolean when passed a Message object + * @arg {Number} options.limit The max number of messages to search through, -1 for no limit + * @arg {String} [options.reason] The reason to be displayed in audit logs + * @returns {Promise} Resolves with the number of messages deleted + */ + purge(options) { + return this.client.purgeChannel.call(this.client, this.id, options); + } + + /** + * Remove a reaction from a message + * @arg {String} messageID The ID of the message + * @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji) + * @arg {String} [userID="@me"] The ID of the user to remove the reaction for + * @returns {Promise} + */ + removeMessageReaction(messageID, reaction, userID) { + return this.client.removeMessageReaction.call(this.client, this.id, messageID, reaction, userID); + } + + /** + * Remove all reactions from a message for a single emoji + * @arg {String} messageID The ID of the message + * @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji) + * @returns {Promise} + */ + removeMessageReactionEmoji(messageID, reaction) { + return this.client.removeMessageReactionEmoji.call(this.client, this.id, messageID, reaction); + } + + /** + * Remove all reactions from a message + * @arg {String} messageID The ID of the message + * @returns {Promise} + */ + removeMessageReactions(messageID) { + return this.client.removeMessageReactions.call(this.client, this.id, messageID); + } + + /** + * Send typing status in the channel + * @returns {Promise} + */ + sendTyping() { + return this.client.sendChannelTyping.call(this.client, this.id); + } + + /** + * Unpin a message + * @arg {String} messageID The ID of the message + * @returns {Promise} + */ + unpinMessage(messageID) { + return this.client.unpinMessage.call(this.client, this.id, messageID); + } + + /** + * Un-send a message. You're welcome Programmix + * @arg {String} messageID The ID of the message + * @returns {Promise} + */ + unsendMessage(messageID) { + return this.client.deleteMessage.call(this.client, this.id, messageID); + } + toJSON(props = []) { return super.toJSON([ "lastMessageID", From 34a6623e6ea170749870eccd09e545c15b495b9d Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 23:42:48 +0100 Subject: [PATCH 34/90] Fix incorrectly named constructor param --- lib/structures/NewsThreadChannel.js | 4 ++-- lib/structures/PrivateThreadChannel.js | 4 ++-- lib/structures/PublicThreadChannel.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/structures/NewsThreadChannel.js b/lib/structures/NewsThreadChannel.js index 7eb981f22..053074517 100644 --- a/lib/structures/NewsThreadChannel.js +++ b/lib/structures/NewsThreadChannel.js @@ -7,8 +7,8 @@ const ThreadChannel = require("./ThreadChannel"); * @extends ThreadChannel */ class NewsThreadChannel extends ThreadChannel { - constructor(data, guild, messageLimit) { - super(data, guild, messageLimit); + constructor(data, client, messageLimit) { + super(data, client, messageLimit); } } diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js index f81f0e017..f865f1250 100644 --- a/lib/structures/PrivateThreadChannel.js +++ b/lib/structures/PrivateThreadChannel.js @@ -7,8 +7,8 @@ const ThreadChannel = require("./ThreadChannel"); * @extends ThreadChannel */ class PrivateThreadChannel extends ThreadChannel { - constructor(data, guild, messageLimit) { - super(data, guild, messageLimit); + constructor(data, client, messageLimit) { + super(data, client, messageLimit); } } diff --git a/lib/structures/PublicThreadChannel.js b/lib/structures/PublicThreadChannel.js index 1fe71ecc4..62cf74a86 100644 --- a/lib/structures/PublicThreadChannel.js +++ b/lib/structures/PublicThreadChannel.js @@ -7,8 +7,8 @@ const ThreadChannel = require("./ThreadChannel"); * @extends ThreadChannel */ class PublicThreadChannel extends ThreadChannel { - constructor(data, guild, messageLimit) { - super(data, guild, messageLimit); + constructor(data, client, messageLimit) { + super(data, client, messageLimit); } } From 9bfe4220ce6ba08f9354b640cd027f1e46009d41 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 23:42:59 +0100 Subject: [PATCH 35/90] Cover channel types in Channel.from --- lib/structures/Channel.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/structures/Channel.js b/lib/structures/Channel.js index e623cadcd..a2a8c062c 100644 --- a/lib/structures/Channel.js +++ b/lib/structures/Channel.js @@ -45,6 +45,15 @@ class Channel extends Base { case ChannelTypes.GUILD_STORE: { return new StoreChannel(data, client); } + case ChannelTypes.GUILD_NEWS_THREAD: { + return new NewsThreadChannel(data, client); + } + case ChannelTypes.GUILD_PUBLIC_THREAD: { + return new PublicThreadChannel(data, client); + } + case ChannelTypes.GUILD_PRIVATE_THREAD: { + return new PrivateThreadChannel(data, client); + } case ChannelTypes.GUILD_STAGE: { return new StageChannel(data, client); } @@ -76,7 +85,10 @@ const CategoryChannel = require("./CategoryChannel"); const GuildChannel = require("./GuildChannel"); const GroupChannel = require("./GroupChannel"); const NewsChannel = require("./NewsChannel"); +const NewsThreadChannel = require("./NewsThreadChannel"); const PrivateChannel = require("./PrivateChannel"); +const PrivateThreadChannel = require("./PrivateThreadChannel"); +const PublicThreadChannel = require("./PublicThreadChannel"); const StageChannel = require("./StageChannel"); const StoreChannel = require("./StoreChannel"); const TextChannel = require("./TextChannel"); From 7678cdec5b6b4b8f52f4597ec37d5dcddb9d0f4f Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 23:44:35 +0100 Subject: [PATCH 36/90] No idea how this trailing whitespace got here --- lib/structures/Guild.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 9962ab6cc..2b8486fda 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -154,7 +154,7 @@ class Guild extends Base { member.id = member.user.id; this.members.add(member, this); } - } + } if(data.stage_instances) { for(const stageInstance of data.stage_instances) { From 3d57bef633b27b4da2b0200444771a91486a199c Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 29 Jun 2021 23:45:02 +0100 Subject: [PATCH 37/90] Fix getChannel using channels prop when getting thread channel --- lib/Client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Client.js b/lib/Client.js index 158098a40..77a6c7706 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1733,7 +1733,7 @@ class Client extends EventEmitter { return this.guilds.get(this.channelGuildMap[channelID]).channels.get(channelID); } if(this.threadGuildMap[channelID] && this.guilds.get(this.threadGuildMap[channelID])) { - return this.guilds.get(this.threadGuildMap[channelID]).channels.get(channelID); + return this.guilds.get(this.threadGuildMap[channelID]).threads.get(channelID); } return this.privateChannels.get(channelID) || this.groupChannels.get(channelID); } From 54376a0da9f933d26d2a675947270c8ef2752a65 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 30 Jun 2021 00:06:03 +0100 Subject: [PATCH 38/90] Support stage instances in audit log --- index.d.ts | 4 ++++ lib/Constants.js | 6 +++++- lib/structures/GuildAuditLogEntry.js | 12 +++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 7223acb4d..403903207 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1202,6 +1202,10 @@ declare namespace Eris { INTEGRATION_CREATE: 80; INTEGRATION_UPDATE: 81; INTEGRATION_DELETE: 82; + + STAGE_INSTANCE_CREATE: 83; + STAGE_INSTANCE_UPDATE: 84; + STAGE_INSTANCE_DELETE: 85; }; ChannelTypes: { GUILD_TEXT: 0; diff --git a/lib/Constants.js b/lib/Constants.js index 8ce0fd2be..36be2fd45 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -191,7 +191,11 @@ module.exports.AuditLogActions = { INTEGRATION_CREATE: 80, INTEGRATION_UPDATE: 81, - INTEGRATION_DELETE: 82 + INTEGRATION_DELETE: 82, + + STAGE_INSTANCE_CREATE: 83, + STAGE_INSTANCE_UPDATE: 84, + STAGE_INSTANCE_DELETE: 85 }; module.exports.MessageActivityTypes = { diff --git a/lib/structures/GuildAuditLogEntry.js b/lib/structures/GuildAuditLogEntry.js index 1ba11833d..9dc34c71d 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)?} channel The channel targeted in the entry, action types 26 (MEMBER_MOVE) and 72/74/75 (MESSAGE_DELETE/PIN/UNPIN) only +* @prop {(CategoryChannel | TextChannel | VoiceChannel | NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)?} 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 @@ -66,7 +66,11 @@ class GuildAuditLogEntry extends Base { this.count = +data.options.count; } if(data.options.channel_id) { - this.channel = guild.channels.get(data.options.channel_id); + if(this.actionType >= 83) { + this.channel = guild.threads.get(data.options.channel_id); + } else { + this.channel = guild.channels.get(data.options.channel_id); + } if(data.options.message_id) { this.message = this.channel && this.channel.messages.get(data.options.message_id) || {id: data.options.message_id}; } @@ -119,8 +123,10 @@ 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 + } 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); } From 83bbbdf7a4be83410974307381f195b13ebcaaf1 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 30 Jun 2021 00:18:53 +0100 Subject: [PATCH 39/90] Update threads to documentation --- index.d.ts | 2 +- lib/gateway/Shard.js | 1 - lib/structures/TextChannel.js | 4 ++++ lib/structures/ThreadChannel.js | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 403903207..8c1120293 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1040,7 +1040,6 @@ declare namespace Eris { interface ThreadMetadata { archiveTimestamp: number; archived: boolean; - archiverID?: string; autoArchiveDuration: number; locked?: boolean; } @@ -2705,6 +2704,7 @@ declare namespace Eris { } export class TextChannel extends GuildChannel implements GuildTextable, Invitable { + defaultAutoArchiveDuration: 60 | 1440 | 4320 | 10080; lastMessageID: string; lastPinTimestamp: number | null; messages: Collection>; diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index f0f788f8e..c1e3548cb 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2114,7 +2114,6 @@ class Shard extends EventEmitter { * @prop {Object} oldChannel.threadMetadata Metadata for the thread * @prop {Number} oldChannel.threadMetadata.archiveTimestamp Timestamp when the thread's archive status was last changed, used for calculating recent activity * @prop {Boolean} oldChannel.threadMetadata.archived Whether the thread is archived - * @prop {String?} oldChannel.threadMetadata.archiverID The ID of the user that last (un)archived the thread * @prop {Number} oldChannel.threadMetadata.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 * @prop {Boolean?} oldChannel.threadMetadata.locked Whether the thread is locked */ diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index e2086efb6..04ced1b00 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -7,6 +7,7 @@ const Message = require("./Message"); /** * Represents a guild text channel. See GuildChannel for more properties and methods. * @extends GuildChannel +* @prop {Number} defaultAutoArchiveDuration The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) * @prop {String} lastMessageID The ID of the last message in this channel * @prop {Number} lastPinTimestamp The timestamp of the last pinned message * @prop {Collection} messages Collection of Messages in this channel @@ -31,6 +32,9 @@ class TextChannel extends GuildChannel { if(data.topic !== undefined) { this.topic = data.topic; } + if(data.default_auto_archive_duration !== undefined) { + this.defaultAutoArchiveDuration = data.default_auto_archive_duration; + } } /** diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 6201380ff..2b3e4c883 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -23,7 +23,6 @@ const ThreadMember = require("./ThreadMember"); * @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 {String?} threadMetadata.archiverID The ID of the user that last (un)archived the thread * @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 */ From 5147bda8bb521e895ed903d99846b7c770902aed Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 30 Jun 2021 09:05:12 +0100 Subject: [PATCH 40/90] Support stage instance invites --- index.d.ts | 9 ++++++++- lib/structures/Invite.js | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 8c1120293..39312c180 100644 --- a/index.d.ts +++ b/index.d.ts @@ -24,7 +24,7 @@ declare namespace Eris { type AnyVoiceChannel = VoiceChannel | StageChannel; type ChannelTypes = Constants["ChannelTypes"][keyof Constants["ChannelTypes"]]; type GuildTextableChannel = TextChannel | NewsChannel | AnyThreadChannel; - type InviteChannel = InvitePartialChannel | Exclude; + type InviteChannel = InvitePartialChannel | Exclude; type PossiblyUncachedTextable = Textable | Uncached; type PossiblyUncachedTextableChannel = TextableChannel | Uncached; type TextableChannel = (GuildTextable & GuildTextableChannel) | (Textable & PrivateChannel); @@ -813,6 +813,12 @@ declare namespace Eris { recipients?: { username: string }[]; type: Exclude; } + interface InviteStageInstance { + members: Member[]; + participantCount: number; + speakerCount: number; + topic: string; + } // Member/User interface FetchMembersOptions { @@ -2320,6 +2326,7 @@ declare namespace Eris { maxUses: CT extends "withMetadata" ? number : null; memberCount: CT extends "withMetadata" | "withoutCount" ? null : number; presenceCount: CT extends "withMetadata" | "withoutCount" ? null : number; + stageInstance: CH extends StageChannel ? InviteStageInstance : null; temporary: CT extends "withMetadata" ? boolean : null; uses: CT extends "withMetadata" ? number : null; constructor(data: BaseData, client: Client); diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js index 33c7c4026..cc6d7dbfb 100644 --- a/lib/structures/Invite.js +++ b/lib/structures/Invite.js @@ -5,7 +5,7 @@ const Guild = require("./Guild"); /** * Represents an invite. Some properties are only available when fetching invites from channels, which requires the Manage Channel permission. -* @prop {TextChannel | NewsChannel | VoiceChannel | GroupChannel | Object} channel Info on the invite channel +* @prop {TextChannel | NewsChannel | VoiceChannel | GroupChannel | StageChannel | Object} channel Info on the invite channel * @prop {String} channel.id The ID of the invite's channel * @prop {String?} channel.name The name of the invite's channel * @prop {Number} channel.type The type of the invite's channel @@ -18,6 +18,7 @@ const Guild = require("./Guild"); * @prop {Number?} maxUses The max number of invite uses * @prop {Number?} memberCount The **approximate** member count for the guild * @prop {Number?} presenceCount The **approximate** presence count for the guild +* @prop {Object?} stageInstance The active public stage instance data for the stage channel this invite is for * @prop {Boolean?} temporary Whether the invite grants temporary membership or not * @prop {Number?} uses The number of invite uses */ @@ -48,6 +49,20 @@ class Invite extends Base { this._createdAt = data.created_at !== undefined ? data.created_at : null; this.presenceCount = data.approximate_presence_count !== undefined ? data.approximate_presence_count : null; this.memberCount = data.approximate_member_count !== undefined ? data.approximate_member_count : null; + if(data.stage_instance !== undefined) { + data.stage_instance.members = data.stage_instance.members.map((m) => { + m.id = m.user.id; + return m; + }); + this.stageInstance = { + members: this.guild.members.update(data.stage_instance.members, this.guild), + participantCount: data.stage_instance.participant_count, + speakerCount: data.stage_instance.speaker_count, + topic: data.stage_instance.topic + }; + } else { + this.stageInstance = null; + } } get createdAt() { From 2b2391826af4c15054689ea181682c43f34e0037 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 30 Jun 2021 09:16:23 +0100 Subject: [PATCH 41/90] Support editing thread default archive duration --- index.d.ts | 11 ++++++----- lib/Client.js | 2 ++ lib/structures/GuildChannel.js | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index 39312c180..848d2e0ef 100644 --- a/index.d.ts +++ b/index.d.ts @@ -70,7 +70,7 @@ declare namespace Eris { type Status = "online" | "idle" | "dnd" | "offline"; // Thread - type EditThreadOptions = Pick; + type AutoArchiveDuration = 60 | 1440 | 4320 | 10080; // Voice type ConverterCommand = "./ffmpeg" | "./avconv" | "ffmpeg" | "avconv"; @@ -114,7 +114,8 @@ declare namespace Eris { } interface EditChannelOptions extends Omit { archived?: boolean; - autoArchiveDuration?: number; + autoArchiveDuration?: AutoArchiveDuration; + defaultAutoArchiveDuration?: AutoArchiveDuration; icon?: string; locked?: boolean; name?: string; @@ -1033,7 +1034,7 @@ declare namespace Eris { threads: T[]; } interface CreateThreadOptions { - autoArchiveDuration: number; + autoArchiveDuration: AutoArchiveDuration; name: string; } interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { @@ -1046,7 +1047,7 @@ declare namespace Eris { interface ThreadMetadata { archiveTimestamp: number; archived: boolean; - autoArchiveDuration: number; + autoArchiveDuration: AutoArchiveDuration; locked?: boolean; } @@ -2711,7 +2712,7 @@ declare namespace Eris { } export class TextChannel extends GuildChannel implements GuildTextable, Invitable { - defaultAutoArchiveDuration: 60 | 1440 | 4320 | 10080; + defaultAutoArchiveDuration: AutoArchiveDuration; lastMessageID: string; lastPinTimestamp: number | null; messages: Collection>; diff --git a/lib/Client.js b/lib/Client.js index 77a6c7706..64c7cc2e0 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -985,6 +985,7 @@ 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 {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.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel @@ -1004,6 +1005,7 @@ class Client extends EventEmitter { archived: options.archived, auto_archive_duration: options.autoArchiveDuration, bitrate: options.bitrate, + default_archive_duration: options.defaultArchiveDuration, icon: options.icon, locked: options.locked, name: options.name, diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index ef93e9310..51acb7ad9 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -73,6 +73,7 @@ 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 {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) From f8f77c6f79cb275c42138415ab88648136e03f6e Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 30 Jun 2021 09:20:15 +0100 Subject: [PATCH 42/90] Consistency --- lib/Client.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Client.js b/lib/Client.js index 64c7cc2e0..24cb350ac 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -715,8 +715,9 @@ class Client extends EventEmitter { */ createThreadWithoutMessage(channelID, options) { return this.requestHandler.request("POST", Endpoints.THREAD_WITHOUT_MESSAGE(channelID), true, { + auto_archive_duration: options.autoArchiveDuration, name: options.name, - auto_archive_duration: options.autoArchiveDuration + type: options.type }).then((channel) => Channel.from(channel, this)); } From 3a1b26a4b32989b02c1b170a17bbf4a0bdc90f3d Mon Sep 17 00:00:00 2001 From: Bsian Date: Fri, 2 Jul 2021 13:09:18 +0100 Subject: [PATCH 43/90] Pass client in param --- lib/gateway/Shard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index c1e3548cb..faa236da7 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2223,7 +2223,7 @@ class Shard extends EventEmitter { * @event Client#stageInstanceCreate * @prop {StageInstance} stageInstance The stage instance */ - this.emit("stageInstanceCreate", guild.stageInstances.add(packet.d)); + this.emit("stageInstanceCreate", guild.stageInstances.add(packet.d, this.client)); break; } case "STAGE_INSTANCE_UPDATE": { From 9fad4dad5708d929ba84ea3544f3f36341b4a4d7 Mon Sep 17 00:00:00 2001 From: Donovan Daniels Date: Mon, 12 Jul 2021 05:04:35 -0500 Subject: [PATCH 44/90] Fix typings for creating thread messages (#15) --- index.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 848d2e0ef..6fd1dae73 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2446,7 +2446,7 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; - createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2518,7 +2518,7 @@ declare namespace Eris { export class PrivateThreadChannel extends ThreadChannel { type: 12; - createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2527,7 +2527,7 @@ declare namespace Eris { export class PublicThreadChannel extends ThreadChannel { type: 10 | 11; - createMessage(content: AdvancedMessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2771,7 +2771,7 @@ declare namespace Eris { type: 10 | 11 | 12; constructor(data: BaseData, client: Client, messageLimit?: number); addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: AdvancedMessageContent, 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; From b75a28ebdb314c24a8448663a325dfd29b7dfbec Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 13 Jul 2021 12:07:26 +0100 Subject: [PATCH 45/90] Prefix client prop --- lib/structures/StageInstance.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/structures/StageInstance.js b/lib/structures/StageInstance.js index be99bdfb2..e13b3e184 100644 --- a/lib/structures/StageInstance.js +++ b/lib/structures/StageInstance.js @@ -14,7 +14,7 @@ const Base = require("./Base"); class StageInstance extends Base { constructor(data, client) { super(data.id); - this.client = client; + this._client = client; this.channel = client.getChannel(data.channel_id) || {id: data.channel_id}; this.guild = client.guilds.get(data.guild_id) || {id: data.guild_id}; this.update(data); @@ -37,7 +37,7 @@ class StageInstance extends Base { * @returns {Promise} */ delete() { - return this.client.deleteStageInstance.call(this.client, this.channel.id); + return this._client.deleteStageInstance.call(this._client, this.channel.id); } /** @@ -48,7 +48,7 @@ class StageInstance extends Base { * @returns {Promise} */ edit(options) { - return this.client.editStageInstance.call(this.client, this.channel.id, options); + return this._client.editStageInstance.call(this._client, this.channel.id, options); } } From b499354bf104c6e24ae9bd1ad1f1c3f8d46de57d Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 30 Jul 2021 22:59:02 +0100 Subject: [PATCH 46/90] Add case for type 21 --- lib/structures/Message.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/structures/Message.js b/lib/structures/Message.js index a19bde178..130fb40a7 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -226,6 +226,9 @@ class Message extends Base { case MessageTypes.APPLICATION_COMMAND: { break; } + case MessageTypes.THREAD_STARTER_MESSAGE: { + break; + } case MessageTypes.GUILD_INVITE_REMINDER: { data.content = "Wondering who to invite?\nStart by inviting anyone who can help you build the server!"; break; From ffb55481294bc44917f118d9a87f9f9b5c7788e7 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 30 Jul 2021 23:54:48 +0100 Subject: [PATCH 47/90] also for 18 --- lib/structures/Message.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 130fb40a7..44ebe6062 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -220,6 +220,9 @@ class Message extends Base { data.content = "This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery."; break; } + case MessageTypes.THREAD_CREATED: { + break; + } case MessageTypes.REPLY: { break; } From 6aaae359f30bf2a690bdc1dade8b0934df62e56a Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 1 Aug 2021 14:46:49 +0100 Subject: [PATCH 48/90] Lint Co-authored-by: iiFDCT <35053522+iiFDCT@users.noreply.github.com> --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 6fd1dae73..cc82ca2ea 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2727,7 +2727,7 @@ declare namespace Eris { createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions): Promise; createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; - createWebhook(options: { name: string; avatar?: string | null}, reason?: string): 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; From 29a5ca877fbe9f8e5107877cb22883241d654952 Mon Sep 17 00:00:00 2001 From: Bsian Date: Sun, 1 Aug 2021 15:36:55 +0100 Subject: [PATCH 49/90] Correct getActiveThreads --- index.d.ts | 18 +++++++++--------- lib/Client.js | 10 ++++++++-- lib/structures/TextChannel.js | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/index.d.ts b/index.d.ts index cc82ca2ea..497ec0372 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1028,7 +1028,7 @@ declare namespace Eris { } // Thread - interface ArchivedThreads { + interface ListedThreads { hasMore: boolean; members: ThreadMember[]; threads: T[]; @@ -1803,9 +1803,9 @@ 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>; + 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; @@ -1834,7 +1834,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 */ @@ -2732,11 +2732,11 @@ declare namespace Eris { 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>; + 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 */ diff --git a/lib/Client.js b/lib/Client.js index 24cb350ac..821e92834 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1686,10 +1686,16 @@ class Client extends EventEmitter { /** * Get all active threads in a channel * @arg {String} channelID The ID of the channel - * @returns {Promise>} + * @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 */ 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)) + }; + }); } /** diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index 04ced1b00..c3be38bca 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -168,8 +168,8 @@ class TextChannel extends GuildChannel { } /** - * Get all active threads in this channel - * @returns {Promise>} + * Get all active threads in this channelchannel + * @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 */ getActiveThreads() { return this.client.getActiveThreads.call(this.client, this.id); From 083f94ebdaa3d1f5ee36adce1b4c6fc002bb23f6 Mon Sep 17 00:00:00 2001 From: Bsian Date: Mon, 2 Aug 2021 17:43:01 +0100 Subject: [PATCH 50/90] Fix permissionsOf when run in stage channels --- lib/structures/GuildChannel.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 51acb7ad9..5c3bf2264 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -5,6 +5,7 @@ const Collection = require("../util/Collection"); const Permission = require("./Permission"); const {Permissions} = require("../Constants"); const PermissionOverwrite = require("./PermissionOverwrite"); +const ThreadChannel = require("./ThreadChannel"); /** * Represents a guild channel. You also probably want to look at CategoryChannel, NewsChannel, StoreChannel, TextChannel, and VoiceChannel. See Channel for extra properties. @@ -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; } From 448dcb0c436f292c623a882f27365a0ce99d480e Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 15:13:44 +0100 Subject: [PATCH 51/90] Typings fixes (class implementations) --- index.d.ts | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index 497ec0372..b790be0c5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,14 +20,14 @@ 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 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 @@ -140,7 +140,7 @@ 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; @@ -187,6 +187,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; @@ -2446,7 +2456,7 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2518,7 +2528,7 @@ declare namespace Eris { export class PrivateThreadChannel extends ThreadChannel { type: 12; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2527,7 +2537,7 @@ declare namespace Eris { export class PublicThreadChannel extends ThreadChannel { type: 10 | 11; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2758,20 +2768,24 @@ 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; From b5a0a8879f726722c59f8f39b14d62af8b94843b Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 15:20:10 +0100 Subject: [PATCH 52/90] Correct typings (message ternary checks) --- index.d.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index b790be0c5..ac6290295 100644 --- a/index.d.ts +++ b/index.d.ts @@ -24,6 +24,7 @@ declare namespace Eris { type AnyVoiceChannel = VoiceChannel | StageChannel; type ChannelTypes = Constants["ChannelTypes"][keyof Constants["ChannelTypes"]]; type GuildTextableChannel = TextChannel | NewsChannel; + type GuildTextableWithThread = GuildTextableChannel | AnyThreadChannel; type InviteChannel = InvitePartialChannel | Exclude; type PossiblyUncachedTextable = Textable | Uncached; type PossiblyUncachedTextableChannel = TextableChannel | Uncached; @@ -2396,11 +2397,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; @@ -2415,7 +2416,7 @@ declare namespace Eris { 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 */ From e84743ac03d303917000900a4ded54736b01d3e4 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 15:23:26 +0100 Subject: [PATCH 53/90] Update stage instance members properly --- lib/structures/Invite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js index cc6d7dbfb..db3364bb7 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 From 1fad0e46bf4ab9155666ba710f9449ea069c5fc3 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 15:44:01 +0100 Subject: [PATCH 54/90] Fix thread parent ID docs --- lib/structures/GuildChannel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 5c3bf2264..45a06a3c6 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -14,7 +14,7 @@ const ThreadChannel = require("./ThreadChannel"); * @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 */ @@ -84,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} */ From 24b04d717687e92fdfed2daed0580f4828a316ed Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 16:19:07 +0100 Subject: [PATCH 55/90] Circular import fix --- lib/structures/GuildChannel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 45a06a3c6..ef5963ab0 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -5,7 +5,6 @@ const Collection = require("../util/Collection"); const Permission = require("./Permission"); const {Permissions} = require("../Constants"); const PermissionOverwrite = require("./PermissionOverwrite"); -const ThreadChannel = require("./ThreadChannel"); /** * Represents a guild channel. You also probably want to look at CategoryChannel, NewsChannel, StoreChannel, TextChannel, and VoiceChannel. See Channel for extra properties. @@ -162,3 +161,5 @@ class GuildChannel extends Channel { } module.exports = GuildChannel; + +const ThreadChannel = require("./ThreadChannel"); From 9f04f8d73c25c51c40511fe2c7898e2be38ee96e Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 3 Aug 2021 21:48:10 +0100 Subject: [PATCH 56/90] Cache all incoming THREAD_UPDATEs --- index.d.ts | 2 +- lib/gateway/Shard.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index ac6290295..fed189358 100644 --- a/index.d.ts +++ b/index.d.ts @@ -579,7 +579,7 @@ declare namespace Eris { (event: "threadListSync", listener: (guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]) => void): T; (event: "threadMembersUpdate", listener: (channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]) => void): T; (event: "threadMemberUpdate", listener: (channel: AnyThreadChannel, member: ThreadMember, oldMember: OldThreadMember) => void): T; - (event: "threadUpdate", listener: (channel: AnyThreadChannel, oldChannel: OldThread) => void): T; + (event: "threadUpdate", listener: (channel: AnyThreadChannel, oldChannel: OldThread | null) => void): T; (event: "typingStart", listener: (channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member) => void): T; (event: "typingStart", listener: (channel: PrivateChannel | Uncached, user: User | Uncached, member: null) => void): T; ( diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index faa236da7..1f90426f1 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2091,6 +2091,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.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client); + this.emit("threadUpdate", channel, null); break; } if(!(channel instanceof ThreadChannel)) { @@ -2108,7 +2111,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 From 4e3a2f604c16991ebd277656e3a6af332615cad6 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 4 Aug 2021 11:58:37 +0100 Subject: [PATCH 57/90] Lint --- index.d.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index fed189358..f6e878376 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1232,9 +1232,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; @@ -2410,9 +2410,9 @@ 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; @@ -2526,7 +2526,7 @@ declare namespace Eris { unpinMessage(messageID: string): Promise; unsendMessage(messageID: string): Promise; } - + export class PrivateThreadChannel extends ThreadChannel { type: 12; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; @@ -2705,16 +2705,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 { @@ -2736,8 +2736,8 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; - createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions): 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; @@ -2810,11 +2810,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 { From 3778a27699ca2a6f18598806fd4bbba842adcd7c Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 13:18:46 +0100 Subject: [PATCH 58/90] Correct defaultAutoArchiveDuration prop names --- lib/Client.js | 4 ++-- lib/structures/GuildChannel.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Client.js b/lib/Client.js index 821e92834..498410a01 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -986,7 +986,7 @@ 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.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel @@ -1006,7 +1006,7 @@ 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, locked: options.locked, name: options.name, diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index ef5963ab0..1f5b11a4a 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -73,7 +73,7 @@ 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.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) From 4e86f70fc9aca45254b1cbec743dcc9c745268b4 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 13:33:57 +0100 Subject: [PATCH 59/90] Support threads in audit log --- index.d.ts | 5 +++++ lib/Client.js | 5 +++-- lib/Constants.js | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index f6e878376..b51ca0774 100644 --- a/index.d.ts +++ b/index.d.ts @@ -712,6 +712,7 @@ declare namespace Eris { interface GuildAuditLog { entries: GuildAuditLogEntry[]; integrations: GuildIntegration[]; + threads: AnyThreadChannel[]; users: User[]; webhooks: Webhook[]; } @@ -1223,6 +1224,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; diff --git a/lib/Client.js b/lib/Client.js index 498410a01..a87696b18 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1813,7 +1813,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") { @@ -1842,9 +1842,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 }; }); diff --git a/lib/Constants.js b/lib/Constants.js index 36be2fd45..d2456bff0 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -195,7 +195,11 @@ module.exports.AuditLogActions = { 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 = { From 098e04c4a3127b4167f3495c2265d7f20c79cff0 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 13:51:20 +0100 Subject: [PATCH 60/90] Fix caching IDs for threadMembersUpdate --- lib/gateway/Shard.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 1f90426f1..e19ae0073 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2203,7 +2203,11 @@ 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) => { + m.thread_id = m.id; + m.id = m.user_id; + 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 From f85da811812aacf45d1a39093e8090dfbc1d3fb9 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 14:10:33 +0100 Subject: [PATCH 61/90] Update member cache on threadMembersUpdate --- lib/gateway/Shard.js | 9 +++++++++ lib/structures/ThreadMember.js | 2 ++ 2 files changed, 11 insertions(+) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index e19ae0073..cdd77229d 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2204,8 +2204,17 @@ class Shard extends EventEmitter { } if(packet.d.added_members) { addedMembers = packet.d.added_members.map((m) => { + 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); + } channel.members.update(m, this.client); }); } diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js index 8a2c17655..91dbb44c5 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) { From a0a75f4300bf54382d066d2ea38037d4fec48986 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 14:10:47 +0100 Subject: [PATCH 62/90] Lint --- index.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index b51ca0774..5900a7088 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1040,11 +1040,6 @@ declare namespace Eris { } // Thread - interface ListedThreads { - hasMore: boolean; - members: ThreadMember[]; - threads: T[]; - } interface CreateThreadOptions { autoArchiveDuration: AutoArchiveDuration; name: string; @@ -1056,6 +1051,11 @@ declare namespace Eris { before?: Date; limit?: number; } + interface ListedThreads { + hasMore: boolean; + members: ThreadMember[]; + threads: T[]; + } interface ThreadMetadata { archiveTimestamp: number; archived: boolean; From bee0cfef0c2f4a395a18b2c23847a065f981c092 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 14:16:30 +0100 Subject: [PATCH 63/90] Doc updates r.e. v10 --- lib/Client.js | 2 +- lib/structures/TextChannel.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Client.js b/lib/Client.js index a87696b18..56ff3d13c 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -710,7 +710,7 @@ class Client extends EventEmitter { * @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 {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) { diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index c3be38bca..779cd2d21 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -106,7 +106,7 @@ class TextChannel extends GuildChannel { * @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 {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) { From b7b7fba58487d26a2785cd226d261b84e687bee8 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 11 Aug 2021 14:48:58 +0100 Subject: [PATCH 64/90] getActiveThreads dep to getActiveGuildThreads --- index.d.ts | 24 +++++++++++++++--------- lib/Client.js | 16 +++++++++++++++- lib/rest/Endpoints.js | 1 + lib/structures/Guild.js | 8 ++++++++ lib/structures/TextChannel.js | 2 +- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 5900a7088..86c8b0e6e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1051,8 +1051,10 @@ declare namespace Eris { before?: Date; limit?: number; } - interface ListedThreads { + interface ListedChannelThreads extends ListedGuildThreads { hasMore: boolean; + } + interface ListedGuildThreads { members: ThreadMember[]; threads: T[]; } @@ -1819,9 +1821,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; @@ -1850,7 +1854,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 */ @@ -2181,6 +2185,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; @@ -2748,11 +2753,12 @@ declare namespace Eris { 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 */ diff --git a/lib/Client.js b/lib/Client.js index 56ff3d13c..b4270b212 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1684,7 +1684,21 @@ 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 {Object} 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 {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 */ diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 16fdf97a8..be2397739 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -76,6 +76,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 2b8486fda..c0dbcad61 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -698,6 +698,14 @@ class Guild extends Base { return this.shard.requestGuildMembers(this.id, options); } + /** + * Get all active threads in this guild + * @returns {Object} 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/TextChannel.js b/lib/structures/TextChannel.js index 779cd2d21..accfbeac5 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -168,7 +168,7 @@ class TextChannel extends GuildChannel { } /** - * Get all active threads in this channelchannel + * [DEPRECATED] Get all active threads in this channel. Use guild.getActiveThreads instead * @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 */ getActiveThreads() { From 000732b2e1f13636a61bc648896134ea3c908713 Mon Sep 17 00:00:00 2001 From: Bsian Date: Tue, 17 Aug 2021 21:24:58 +0100 Subject: [PATCH 65/90] Seem to have somehow forgotten to remove this --- lib/structures/ThreadChannel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 2b3e4c883..e6ae94024 100644 --- a/lib/structures/ThreadChannel.js +++ b/lib/structures/ThreadChannel.js @@ -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 }; From 134f5b9c19eeabb199997fa84d9909ae4209565c Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 18 Aug 2021 00:14:53 +0100 Subject: [PATCH 66/90] PrivateThread invitable --- index.d.ts | 15 ++++++++++++--- lib/Client.js | 4 ++++ lib/structures/GuildChannel.js | 1 + lib/structures/PrivateThreadChannel.js | 19 +++++++++++++++++++ lib/structures/TextChannel.js | 1 + lib/structures/ThreadChannel.js | 2 +- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index 86c8b0e6e..571b06b8f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -118,6 +118,7 @@ declare namespace Eris { autoArchiveDuration?: AutoArchiveDuration; defaultAutoArchiveDuration?: AutoArchiveDuration; icon?: string; + invitable?: boolean; locked?: boolean; name?: string; ownerID?: string; @@ -1044,8 +1045,9 @@ declare namespace Eris { 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; @@ -1058,6 +1060,9 @@ declare namespace Eris { members: ThreadMember[]; threads: T[]; } + interface PrivateThreadMetadata extends ThreadMetadata { + invitable: boolean; + } interface ThreadMetadata { archiveTimestamp: number; archived: boolean; @@ -2468,6 +2473,7 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; 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[]>; @@ -2538,8 +2544,10 @@ declare namespace Eris { } 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[]>; @@ -2549,6 +2557,7 @@ declare namespace Eris { 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[]>; @@ -2800,7 +2809,7 @@ declare namespace Eris { 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>; diff --git a/lib/Client.js b/lib/Client.js index b4270b212..4d5964b27 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -709,6 +709,7 @@ 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 to explicitly set this property as this will be a required property in API v10 * @returns {Promise} @@ -716,6 +717,7 @@ class Client extends EventEmitter { 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)); @@ -988,6 +990,7 @@ class Client extends EventEmitter { * @arg {Number} [options.bitrate] The bitrate of the channel (guild voice 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) @@ -1008,6 +1011,7 @@ class Client extends EventEmitter { bitrate: options.bitrate, default_auto_archive_duration: options.defaultAutoArchiveDuration, icon: options.icon, + invitable: options.invitable, locked: options.locked, name: options.name, nsfw: options.nsfw, diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 1f5b11a4a..b0f3052d1 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -74,6 +74,7 @@ class GuildChannel extends Channel { * @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.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) diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js index f865f1250..69643e6d2 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 accfbeac5..3c09e097f 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -105,6 +105,7 @@ 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 to explicitly set this property as this will be a required property in API v10 * @returns {Promise} diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index e6ae94024..75c32e212 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) { From afad66e46e41493e3d09d02c8811c4eb92fb6953 Mon Sep 17 00:00:00 2001 From: Bsian Date: Wed, 18 Aug 2021 00:25:54 +0100 Subject: [PATCH 67/90] Send messages in threads permission --- index.d.ts | 9 +++--- lib/Constants.js | 79 +++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/index.d.ts b/index.d.ts index 571b06b8f..c0270ddb7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1370,12 +1370,13 @@ declare namespace Eris { useSlashCommands: 2147483648n; voiceRequestToSpeak: 4294967296n; manageThreads: 17179869184n; - usePublicThreads: 34359738368n; - usePrivateThreads: 68719476736n; + createPublicThreads: 34359738368n; + createPrivateThreads: 68719476736n; + sendMessagesInThreads: 274877906944n; allGuild: 2080899262n; - allText: 123212397649n; + allText: 398090304593n; allVoice: 4629464849n; - all: 128849018879n; + all: 403726925823n; }; REST_VERSION: 9; StickerFormats: { diff --git a/lib/Constants.js b/lib/Constants.js index d2456bff0..33f9d0b8f 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -34,43 +34,45 @@ module.exports.GATEWAY_VERSION = 9; module.exports.REST_VERSION = 9; const Permissions = { - createInstantInvite: 1n, - kickMembers: 1n << 1n, - banMembers: 1n << 2n, - administrator: 1n << 3n, - manageChannels: 1n << 4n, - manageGuild: 1n << 5n, - addReactions: 1n << 6n, - viewAuditLog: 1n << 7n, viewAuditLogs: 1n << 7n, // [DEPRECATED] - voicePrioritySpeaker: 1n << 8n, - voiceStream: 1n << 9n, stream: 1n << 9n, // [DEPRECATED] - viewChannel: 1n << 10n, readMessages: 1n << 10n, // [DEPRECATED] - sendMessages: 1n << 11n, - sendTTSMessages: 1n << 12n, - manageMessages: 1n << 13n, - embedLinks: 1n << 14n, - attachFiles: 1n << 15n, - readMessageHistory: 1n << 16n, - mentionEveryone: 1n << 17n, - useExternalEmojis: 1n << 18n, externalEmojis: 1n << 18n, // [DEPRECATED] - viewGuildInsights: 1n << 19n, - voiceConnect: 1n << 20n, - voiceSpeak: 1n << 21n, - voiceMuteMembers: 1n << 22n, - voiceDeafenMembers: 1n << 23n, - voiceMoveMembers: 1n << 24n, - voiceUseVAD: 1n << 25n, - changeNickname: 1n << 26n, - manageNicknames: 1n << 27n, - manageRoles: 1n << 28n, - manageWebhooks: 1n << 29n, - manageEmojis: 1n << 30n, - useSlashCommands: 1n << 31n, - voiceRequestToSpeak: 1n << 32n, + createInstantInvite: 1n, + kickMembers: 1n << 1n, + banMembers: 1n << 2n, + administrator: 1n << 3n, + manageChannels: 1n << 4n, + manageGuild: 1n << 5n, + addReactions: 1n << 6n, + viewAuditLog: 1n << 7n, viewAuditLogs: 1n << 7n, // [DEPRECATED] + voicePrioritySpeaker: 1n << 8n, + voiceStream: 1n << 9n, stream: 1n << 9n, // [DEPRECATED] + viewChannel: 1n << 10n, readMessages: 1n << 10n, // [DEPRECATED] + sendMessages: 1n << 11n, + sendTTSMessages: 1n << 12n, + manageMessages: 1n << 13n, + embedLinks: 1n << 14n, + attachFiles: 1n << 15n, + readMessageHistory: 1n << 16n, + mentionEveryone: 1n << 17n, + useExternalEmojis: 1n << 18n, externalEmojis: 1n << 18n, // [DEPRECATED] + viewGuildInsights: 1n << 19n, + voiceConnect: 1n << 20n, + voiceSpeak: 1n << 21n, + voiceMuteMembers: 1n << 22n, + voiceDeafenMembers: 1n << 23n, + voiceMoveMembers: 1n << 24n, + voiceUseVAD: 1n << 25n, + changeNickname: 1n << 26n, + manageNicknames: 1n << 27n, + manageRoles: 1n << 28n, + manageWebhooks: 1n << 29n, + manageEmojis: 1n << 30n, + useSlashCommands: 1n << 31n, + voiceRequestToSpeak: 1n << 32n, - manageThreads: 1n << 34n, - usePublicThreads: 1n << 35n, - usePrivateThreads: 1n << 36n + manageThreads: 1n << 34n, + createPublicThreads: 1n << 35n, + createPrivateThreads: 1n << 36n, + + sendMessagesinThreads: 1n << 38n }; Permissions.allGuild = Permissions.kickMembers | Permissions.banMembers @@ -100,8 +102,9 @@ Permissions.allText = Permissions.createInstantInvite | Permissions.manageWebhooks | Permissions.useSlashCommands | Permissions.manageThreads - | Permissions.usePublicThreads - | Permissions.usePrivateThreads; + | Permissions.createPublicThreads + | Permissions.createPrivateThreads + | Permissions.sendMessagesinThreads; Permissions.allVoice = Permissions.createInstantInvite | Permissions.manageChannels | Permissions.voicePrioritySpeaker From 890465d7112d00b073ff934f211db469c24649db Mon Sep 17 00:00:00 2001 From: Bsian Date: Fri, 20 Aug 2021 19:27:03 +0100 Subject: [PATCH 68/90] added thread member presence is nullable --- lib/gateway/Shard.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index cdd77229d..008b2b944 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2204,8 +2204,10 @@ class Shard extends EventEmitter { } if(packet.d.added_members) { addedMembers = packet.d.added_members.map((m) => { - m.presence.id = m.presence.user.id; - this.client.users.update(m.presence.user, this.client); + 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; From 74653343c0d194ef453e6dc9c3c60fac1f31846b Mon Sep 17 00:00:00 2001 From: Bsian Date: Sun, 22 Aug 2021 23:11:54 +0100 Subject: [PATCH 69/90] Create separate route for messages < 10s --- lib/rest/RequestHandler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rest/RequestHandler.js b/lib/rest/RequestHandler.js index 4a4a0db72..f1c51ff69 100644 --- a/lib/rest/RequestHandler.js +++ b/lib/rest/RequestHandler.js @@ -396,6 +396,8 @@ class RequestHandler { const createdAt = Base.getCreatedAt(messageID); if(Date.now() - this.latencyRef.latency - createdAt >= 1000 * 60 * 60 * 24 * 14) { method += "_OLD"; + } else if(Date.now() - this.latencyRef.latency - createdAt <= 1000 * 10) { + method += "_NEW"; } route = method + route; } From f3a44356439ed626d31c65ac68b1a429e42f1387 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sat, 28 Aug 2021 23:59:50 +0100 Subject: [PATCH 70/90] Documentation fixes --- lib/Client.js | 8 ++++---- lib/structures/Guild.js | 2 +- lib/structures/TextChannel.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Client.js b/lib/Client.js index 1ab66c6de..2de88cfd0 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1761,7 +1761,7 @@ class Client extends EventEmitter { /** * Get all active threads in a guild * @arg {String} guildID The ID of the guild - * @returns {Object} An object containing an array of `threads` and an array of `members` + * @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) => { @@ -1775,7 +1775,7 @@ class Client extends EventEmitter { /** * [DEPRECATED] Get all active threads in a channel. Use getActiveGuildThreads instead * @arg {String} channelID The ID of the channel - * @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 */ getActiveThreads(channelID) { return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((response) => { @@ -1794,7 +1794,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) => { @@ -2098,7 +2098,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) => { diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index f09df6e40..77017773c 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -700,7 +700,7 @@ class Guild extends Base { /** * Get all active threads in this guild - * @returns {Object} An object containing an array of `threads` and an array of `members` + * @returns {Promise} An object containing an array of `threads` and an array of `members` */ getActiveThreads() { return this.client.getActiveThreads.call(this.client, this.id); diff --git a/lib/structures/TextChannel.js b/lib/structures/TextChannel.js index f70e4db7f..259b5fa0d 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -204,7 +204,7 @@ class TextChannel extends GuildChannel { /** * [DEPRECATED] Get all active threads in this channel. Use guild.getActiveThreads instead - * @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 */ getActiveThreads() { return this.client.getActiveThreads.call(this.client, this.id); @@ -216,7 +216,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); @@ -235,7 +235,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); From 9bb0a17f8f14ad3d942897146c81cf3f47db7802 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Tue, 31 Aug 2021 22:32:14 +0100 Subject: [PATCH 71/90] Thread metadata locked guaranteed --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 33688718e..c30d1784b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1105,7 +1105,7 @@ declare namespace Eris { archiveTimestamp: number; archived: boolean; autoArchiveDuration: AutoArchiveDuration; - locked?: boolean; + locked: boolean; } // Voice From a8d9a6ae522e2751fed94c7fdc196d977bb01f64 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Tue, 31 Aug 2021 22:33:46 +0100 Subject: [PATCH 72/90] Correct permission name --- lib/Constants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Constants.js b/lib/Constants.js index ef4a0d54f..ec3c60208 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -72,7 +72,7 @@ const Permissions = { createPublicThreads: 1n << 35n, createPrivateThreads: 1n << 36n, useExternalStickers: 1n << 37n, - sendMessagesinThreads: 1n << 38n + sendMessagesInThreads: 1n << 38n }; Permissions.allGuild = Permissions.kickMembers | Permissions.banMembers @@ -105,7 +105,7 @@ Permissions.allText = Permissions.createInstantInvite | Permissions.createPublicThreads | Permissions.createPrivateThreads | Permissions.useExternalStickers - | Permissions.sendMessagesinThreads; + | Permissions.sendMessagesInThreads; Permissions.allVoice = Permissions.createInstantInvite | Permissions.manageChannels | Permissions.voicePrioritySpeaker From 46eb13ceb83cb37ea943a4e195006af87ceffd96 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Wed, 1 Sep 2021 01:07:44 +0100 Subject: [PATCH 73/90] Prevent heartbeat during identify --- index.d.ts | 2 +- lib/gateway/Shard.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index aec24bb95..d2b9e341d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2555,7 +2555,7 @@ declare namespace Eris { requestMembersPromise: { [s: string]: RequestMembersPromise }; seq: number; sessionID: string | null; - status: "disconnected" | "connecting" | "handshaking" | "ready" | "resuming"; + status: "connecting" | "disconnected" | "handshaking" | "identifying" | "ready" | "resuming"; unsyncedGuilds: number; ws: WebSocket | BrowserWebSocket | null; constructor(id: number, client: Client); diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 70c4bd78f..9f06836ce 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -48,7 +48,7 @@ try { * @prop {Number} lastHeartbeatSent Last time shard sent a heartbeat, null if shard has not sent heartbeat yet * @prop {Number} latency The current latency between the shard and Discord, in milliseconds * @prop {Boolean} ready Whether the shard is ready -* @prop {String} status The status of the shard. "disconnected"/"connecting"/"handshaking"/"ready" +* @prop {String} status The status of the shard. "disconnected"/"connecting"/"handshaking"/"ready"/"identifying"/"resuming" */ class Shard extends EventEmitter { constructor(id, client) { @@ -286,8 +286,8 @@ class Shard extends EventEmitter { } heartbeat(normal) { - // Can only heartbeat after resume succeeds, discord/discord-api-docs#1619 - if(this.status === "resuming") { + // Cannot heartbeat during an identify/resume otherwise session will be killed + if(this.status === "resuming" || this.status === "identifying") { return; } if(normal) { @@ -320,6 +320,7 @@ class Shard extends EventEmitter { this.emit("error", new Error("pako/zlib-sync not found, cannot decompress data")); return; } + this.status = "identifying"; const identify = { token: this._token, v: GATEWAY_VERSION, From 1570b7481db2d1a141e49a48207b327867c66328 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Thu, 2 Sep 2021 19:22:06 +0100 Subject: [PATCH 74/90] notes man --- lib/gateway/Shard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 9f06836ce..c66664550 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -286,7 +286,7 @@ class Shard extends EventEmitter { } heartbeat(normal) { - // Cannot heartbeat during an identify/resume otherwise session will be killed + // Can only heartbeat after identify/resume succeeds, session will be killed otherwise, discord/discord-api-docs#1619 if(this.status === "resuming" || this.status === "identifying") { return; } From 3e3f6239aea78f9e8212d7dfeddc3a882202ccf7 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 3 Sep 2021 01:42:13 +0100 Subject: [PATCH 75/90] Fix not sending newly cached thread channel --- lib/gateway/Shard.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 6ea9d7c08..eb02ad706 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2091,10 +2091,10 @@ class Shard extends EventEmitter { break; } case "THREAD_UPDATE": { - const channel = this.client.getChannel(packet.d.id); + let channel = this.client.getChannel(packet.d.id); if(!channel) { const thread = Channel.from(packet.d, this.client); - this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client); + channel = this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client); this.emit("threadUpdate", channel, null); break; } @@ -2109,7 +2109,7 @@ class Shard extends EventEmitter { }; channel.update(packet.d); - /** + /**b * Fired when a thread channel is updated * @event Client#threadUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel From c09be80304ff08a31274205f608b35ea1288a4b0 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 3 Sep 2021 01:43:56 +0100 Subject: [PATCH 76/90] This is a better fix --- lib/gateway/Shard.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index eb02ad706..87ff43eac 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2091,11 +2091,10 @@ class Shard extends EventEmitter { break; } case "THREAD_UPDATE": { - let channel = this.client.getChannel(packet.d.id); + const channel = this.client.getChannel(packet.d.id); if(!channel) { const thread = Channel.from(packet.d, this.client); - channel = this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client); - this.emit("threadUpdate", channel, null); + this.emit("threadUpdate", this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client), null); break; } if(!(channel instanceof ThreadChannel)) { From 69d5acc341d3295c559c72de8b7c2e1ff08412eb Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 3 Sep 2021 01:44:18 +0100 Subject: [PATCH 77/90] No one saw this --- lib/gateway/Shard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 87ff43eac..c96e0ff42 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2108,7 +2108,7 @@ class Shard extends EventEmitter { }; channel.update(packet.d); - /**b + /** * Fired when a thread channel is updated * @event Client#threadUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel From fbcbb25c1cc3a199523458c3c9cc5199df939ffb Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 3 Sep 2021 02:14:48 +0100 Subject: [PATCH 78/90] Add thread channel ID to guild map on thread update --- lib/gateway/Shard.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index c96e0ff42..1ee576b83 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2095,6 +2095,7 @@ class Shard extends EventEmitter { 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)) { From 65cb0ab22a74691747a4d24e893a7b3049e35ac1 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Fri, 3 Sep 2021 02:31:17 +0100 Subject: [PATCH 79/90] Actually return the member --- lib/gateway/Shard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 1ee576b83..19ded167c 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2219,7 +2219,7 @@ class Shard extends EventEmitter { guild.members.update(m.presence, guild); guild.members.update(m.member); } - channel.members.update(m, this.client); + return channel.members.update(m, this.client); }); } /** From cfe55cdf4112feabd73b320b0eeffb19064fbadf Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 3 Oct 2021 21:38:20 +0100 Subject: [PATCH 80/90] Shard calculation fallback --- lib/structures/Base.js | 6 +++++- lib/structures/Guild.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/structures/Base.js b/lib/structures/Base.js index ae16245d9..ed40290c7 100644 --- a/lib/structures/Base.js +++ b/lib/structures/Base.js @@ -14,7 +14,11 @@ class Base { } static getCreatedAt(id) { - return Math.floor(id / 4194304) + 1420070400000; + return Base.getDiscordEpoch(id) + 1420070400000; + } + + static getDiscordEpoch(id) { + return Math.floor(id / 4194304); } [util.inspect.custom]() { diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 77017773c..97204324a 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -79,7 +79,7 @@ class Guild extends Base { constructor(data, client) { super(data.id); this._client = client; - this.shard = client.shards.get(client.guildShardMap[this.id]); + this.shard = client.shards.get(client.guildShardMap[this.id] || (Base.getDiscordEpoch(data.id) % client.options.lastShardID) || 0); this.unavailable = !!data.unavailable; this.joinedAt = Date.parse(data.joined_at); this.voiceStates = new Collection(VoiceState); From bfeb5e35a0cc86e4444c780727d0d586d5e25b5e Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 3 Oct 2021 21:45:20 +0100 Subject: [PATCH 81/90] Reorder threadMembersUpdate --- lib/gateway/Shard.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 19ded167c..62df6f178 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2199,11 +2199,8 @@ class Shard extends EventEmitter { break; } channel.update(packet.d); - let removedMembers; let addedMembers; - if(packet.d.removed_member_ids) { - removedMembers = packet.d.removed_member_ids.map((id) => channel.members.remove({id}) || {id}); - } + let removedMembers; if(packet.d.added_members) { addedMembers = packet.d.added_members.map((m) => { if(m.presence) { @@ -2222,14 +2219,17 @@ class Shard extends EventEmitter { return channel.members.update(m, this.client); }); } + if(packet.d.removed_member_ids) { + removedMembers = packet.d.removed_member_ids.map((id) => channel.members.remove({id}) || {id}); + } /** * 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 * @event Client#threadMembersUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The thread channel - * @prop {Array} removedMembers An array of members that were removed from the thread channel. If a member is uncached, it will be an object with an `id` key. No other property is guaranteed - * @prop {Array} addedMembers An array of members that were added to the thread channel + * @prop {Array} [addedMembers] An array of members that were added to the thread channel + * @prop {Array} [removedMembers] An array of members that were removed from the thread channel. If a member is uncached, it will be an object with an `id` key. No other property is guaranteed */ - this.emit("threadMembersUpdate", channel, removedMembers, addedMembers); + this.emit("threadMembersUpdate", channel, addedMembers, removedMembers); break; } case "STAGE_INSTANCE_CREATE": { From c6a1b72cf56c16b71865cfbcc667d45506b0ba5d Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 3 Oct 2021 21:47:37 +0100 Subject: [PATCH 82/90] Typing for previous commit --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index ec17d2937..ea739f4ad 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1567,6 +1567,7 @@ declare namespace Eris { id: string; constructor(id: string); static getCreatedAt(id: string): number; + static getDiscordEpoch(id: string): number; inspect(): this; toString(): string; toJSON(props?: string[]): JSONCache; From 7630bf9e1d3e381f391513cbfceee7906492168f Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 3 Oct 2021 21:49:03 +0100 Subject: [PATCH 83/90] Actually do this with threadMembersUpdate --- index.d.ts | 2 +- lib/gateway/Shard.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index ea739f4ad..b24d36c9f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -579,7 +579,7 @@ declare namespace Eris { threadCreate: [channel: AnyThreadChannel]; threadDelete: [channel: AnyThreadChannel]; threadListSync: [guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]]; - threadMembersUpdate: [channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]]; + threadMembersUpdate: [channel: AnyThreadChannel, addedMembers: ThreadMember[], removedMembers: (ThreadMember | Uncached)[]]; threadMemberUpdate: [channel: AnyThreadChannel, member: ThreadMember, oldMember: OldThreadMember]; threadUpdate: [channel: AnyThreadChannel, oldChannel: OldThread | null]; typingStart: [channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member] diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 62df6f178..5e7a34d81 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2226,10 +2226,10 @@ class Shard extends EventEmitter { * 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 * @event Client#threadMembersUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The thread channel - * @prop {Array} [addedMembers] An array of members that were added to the thread channel - * @prop {Array} [removedMembers] An array of members that were removed from the thread channel. If a member is uncached, it will be an object with an `id` key. No other property is guaranteed + * @prop {Array} addedMembers An array of members that were added to the thread channel + * @prop {Array} removedMembers An array of members that were removed from the thread channel. If a member is uncached, it will be an object with an `id` key. No other property is guaranteed */ - this.emit("threadMembersUpdate", channel, addedMembers, removedMembers); + this.emit("threadMembersUpdate", channel, addedMembers || [], removedMembers || []); break; } case "STAGE_INSTANCE_CREATE": { From 29cc1055157539c114d4c23673586003fa8ea5bf Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sun, 3 Oct 2021 21:50:02 +0100 Subject: [PATCH 84/90] Correct createThreadWithMessage typing Co-authored-by: Lava --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index b24d36c9f..61fa25b45 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1793,7 +1793,7 @@ declare namespace Eris { createMessage(channelID: string, content: MessageContent, file?: MessageFile | MessageFile[]): Promise; createRole(guildID: string, options?: RoleOptions | Role, reason?: string): Promise; createStageInstance(channelID: string, options: StageInstanceOptions): Promise; - createThreadWithMessage(channelID: string, options: CreateThreadOptions): Promise; + createThreadWithMessage(channelID: string, messageID: string, options: CreateThreadOptions): Promise; createThreadWithoutMessage(channelID: string, options: CreateThreadWithoutMessageOptions): Promise; crosspostMessage(channelID: string, messageID: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; From 1bcc6dbc584a9e2bbc264f457821bc17b01a2655 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Tue, 2 Nov 2021 16:51:23 +0000 Subject: [PATCH 85/90] Lint --- index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 03f518d71..c1d55d5e3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -729,8 +729,8 @@ declare namespace Eris { threadMembersUpdate: [channel: AnyThreadChannel, addedMembers: ThreadMember[], removedMembers: (ThreadMember | Uncached)[]]; 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]; + typingStart: [channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member] + | [channel: PrivateChannel | Uncached, user: User | Uncached, member: null]; unavailableGuildCreate: [guild: UnavailableGuild]; unknown: [packet: RawPacket, id: number]; userUpdate: [user: User, oldUser: PartialUser | null]; @@ -739,7 +739,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]; From f67a14f68186f25677e0529725a211e0beb28e30 Mon Sep 17 00:00:00 2001 From: Emad Date: Thu, 11 Nov 2021 22:03:48 +0300 Subject: [PATCH 86/90] rawWS fix data.resolved overwrite in CommandInteraction --- lib/structures/CommandInteraction.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index c8e596cc0..2fd52a6ba 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -41,9 +41,22 @@ class CommandInteraction extends Interaction { id: info.channel_id }; - this.data = info.data; + this.data = { + id: info.data.id, + name: info.data.name, + type: info.data.type, + target_id: info.data.target_id, + }; + + if(info.data.options) { + this.data.options = { + name: info.data.options.name, + type: info.data.options.type + } + } if(info.data.resolved !== undefined) { + this.data.resolved = {}; //Users if(info.data.resolved.users !== undefined) { const usermap = new Collection(User); From 5416bf287bd80923fc8bf9a63526124315800b0f Mon Sep 17 00:00:00 2001 From: Emad Date: Thu, 11 Nov 2021 22:12:28 +0300 Subject: [PATCH 87/90] lint --- lib/structures/CommandInteraction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index 2fd52a6ba..0c8ca0185 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -52,7 +52,7 @@ class CommandInteraction extends Interaction { this.data.options = { name: info.data.options.name, type: info.data.options.type - } + }; } if(info.data.resolved !== undefined) { From c1131f3aa0c990a627fa4319dfbdf969fc741c70 Mon Sep 17 00:00:00 2001 From: Emad Date: Thu, 11 Nov 2021 22:19:15 +0300 Subject: [PATCH 88/90] eslint! --- lib/structures/CommandInteraction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index 0c8ca0185..d5273a9f7 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -45,7 +45,7 @@ class CommandInteraction extends Interaction { id: info.data.id, name: info.data.name, type: info.data.type, - target_id: info.data.target_id, + target_id: info.data.target_id }; if(info.data.options) { From 7fda3230f4015eb86b5ffe158693b76536010951 Mon Sep 17 00:00:00 2001 From: Emad Date: Thu, 11 Nov 2021 22:26:30 +0300 Subject: [PATCH 89/90] missing value& options --- lib/structures/CommandInteraction.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index d5273a9f7..6867e6a1c 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -51,7 +51,9 @@ class CommandInteraction extends Interaction { if(info.data.options) { this.data.options = { name: info.data.options.name, - type: info.data.options.type + type: info.data.options.type, + value: info.data.options.value, + options: info.data.options.options }; } From bc417c5db0f4769b6ce898666559af642c6838ab Mon Sep 17 00:00:00 2001 From: Emad Date: Sat, 13 Nov 2021 15:50:36 +0300 Subject: [PATCH 90/90] Fix CommandInteraction and rawWS deep reference --- lib/structures/CommandInteraction.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/structures/CommandInteraction.js b/lib/structures/CommandInteraction.js index 6867e6a1c..51db4c99b 100644 --- a/lib/structures/CommandInteraction.js +++ b/lib/structures/CommandInteraction.js @@ -41,24 +41,9 @@ class CommandInteraction extends Interaction { id: info.channel_id }; - this.data = { - id: info.data.id, - name: info.data.name, - type: info.data.type, - target_id: info.data.target_id - }; - - if(info.data.options) { - this.data.options = { - name: info.data.options.name, - type: info.data.options.type, - value: info.data.options.value, - options: info.data.options.options - }; - } + this.data = JSON.parse(JSON.stringify(info.data)); if(info.data.resolved !== undefined) { - this.data.resolved = {}; //Users if(info.data.resolved.users !== undefined) { const usermap = new Collection(User);