diff --git a/index.d.ts b/index.d.ts index 485a9be76..2051527d1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,14 +20,15 @@ declare namespace Eris { // Channel type AnyChannel = AnyGuildChannel | PrivateChannel; type AnyGuildChannel = GuildTextableChannel | AnyVoiceChannel | CategoryChannel | StoreChannel; - type AnyThreadChannel = NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel; + type AnyThreadChannel = NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel | ThreadChannel; type AnyVoiceChannel = VoiceChannel | StageChannel; type ChannelTypes = Constants["ChannelTypes"][keyof Constants["ChannelTypes"]]; - type GuildTextableChannel = TextChannel | NewsChannel | AnyThreadChannel; + type GuildTextableChannel = TextChannel | NewsChannel; + type GuildTextableWithThread = GuildTextableChannel | AnyThreadChannel; type InviteChannel = InvitePartialChannel | Exclude; type PossiblyUncachedTextable = Textable | Uncached; type PossiblyUncachedTextableChannel = TextableChannel | Uncached; - type TextableChannel = (GuildTextable & GuildTextableChannel) | (Textable & PrivateChannel); + type TextableChannel = (GuildTextable & GuildTextableChannel) | (ThreadTextable & AnyThreadChannel) | (Textable & PrivateChannel); type VideoQualityMode = 1 | 2; // Command @@ -131,6 +132,7 @@ declare namespace Eris { autoArchiveDuration?: AutoArchiveDuration; defaultAutoArchiveDuration?: AutoArchiveDuration; icon?: string; + invitable?: boolean; locked?: boolean; name?: string; ownerID?: string; @@ -154,14 +156,14 @@ declare namespace Eris { createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; getWebhooks(): Promise; - purge(limit: number, filter?: (message: Message) => boolean, before?: string, after?: string, reason?: string): Promise; + purge(options: PurgeChannelOptions): Promise; removeMessageReactionEmoji(messageID: string, reaction: string): Promise; removeMessageReactions(messageID: string): Promise; sendTyping(): Promise; } interface PartialChannel { bitrate?: number; - id?: number; + id: string; name?: string; nsfw?: boolean; parent_id?: number; @@ -201,6 +203,16 @@ declare namespace Eris { unpinMessage(messageID: string): Promise; unsendMessage(messageID: string): Promise; } + // @ts-ignore ts(2430) - ThreadTextable can't properly extend Textable because of getMessageReaction deprecated overload + interface ThreadTextable extends Textable { + lastPinTimestamp?: number; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } interface WebhookData { channelID: string; guildID: string; @@ -563,6 +575,15 @@ declare namespace Eris { relationshipAdd: [relationship: Relationship]; relationshipRemove: [relationship: Relationship]; relationshipUpdate: [relationship: Relationship, oldRelationship: { type: number }]; + stageInstanceCreate: [stageInstance: StageInstance]; + stageInstanceDelete: [stageInstance: StageInstance]; + stageInstanceUpdate: [stageInstance: StageInstance, oldStageInstance: OldStageInstance | null]; + threadCreate: [channel: AnyThreadChannel]; + threadDelete: [channel: AnyThreadChannel]; + threadListSync: [guild: Guild, deletedThreads: (AnyThreadChannel | Uncached)[], activeThreads: AnyThreadChannel[], joinedThreadsMember: ThreadMember[]]; + threadMembersUpdate: [channel: AnyThreadChannel, removedMembers: (ThreadMember | Uncached)[], addedMembers: ThreadMember[]]; + threadMemberUpdate: [channel: AnyThreadChannel, member: ThreadMember, oldMember: OldThreadMember]; + threadUpdate: [channel: AnyThreadChannel, oldChannel: OldThread | null]; typingStart: [channel: GuildTextableChannel | Uncached, user: User | Uncached, member: Member] | [channel: PrivateChannel | Uncached, user: User | Uncached, member: null]; unavailableGuildCreate: [guild: UnavailableGuild]; @@ -573,7 +594,7 @@ declare namespace Eris { voiceChannelSwitch: [member: Member, newChannel: AnyVoiceChannel, oldChannel: AnyVoiceChannel]; voiceStateUpdate: [member: Member, oldState: OldVoiceState]; warn: [message: string, id: number]; - webhooksUpdate: [data: WebhookData]; + webhooksUpdate: [data: WebhookData]; } interface ClientEvents extends EventListeners { shardDisconnect: [err: Error | undefined, id: number]; @@ -698,6 +719,7 @@ declare namespace Eris { interface GuildAuditLog { entries: GuildAuditLogEntry[]; integrations: GuildIntegration[]; + threads: AnyThreadChannel[]; users: User[]; webhooks: Webhook[]; } @@ -869,6 +891,7 @@ declare namespace Eris { } interface AdvancedMessageContent { allowedMentions?: AllowedMentions; + components?: ActionRow[]; content?: string; /** @deprecated */ embed?: EmbedOptions; @@ -877,6 +900,7 @@ declare namespace Eris { messageReference?: MessageReferenceReply; /** @deprecated */ messageReferenceID?: string; + stickerIDs?: string[]; tts?: boolean; } interface AdvancedMessageContentEdit extends AdvancedMessageContent { @@ -1039,7 +1063,7 @@ declare namespace Eris { interface PartialRole { color?: number; hoist?: boolean; - id?: number; + id: string; mentionable?: boolean; name?: string; permissions?: number; @@ -1059,34 +1083,50 @@ declare namespace Eris { } // Thread - interface ArchivedThreads { - hasMore: boolean; - members: ThreadMember[]; - threads: T[]; - } interface CreateThreadOptions { autoArchiveDuration: AutoArchiveDuration; name: string; } - interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { - type: AnyThreadChannel["type"]; + interface CreateThreadWithoutMessageOptions extends CreateThreadOptions { + invitable: T extends PrivateThreadChannel["type"] ? boolean : never; + type: T; } interface GetArchivedThreadsOptions { before?: Date; limit?: number; } + interface ListedChannelThreads extends ListedGuildThreads { + hasMore: boolean; + } + interface ListedGuildThreads { + members: ThreadMember[]; + threads: T[]; + } + interface PrivateThreadMetadata extends ThreadMetadata { + invitable: boolean; + } interface ThreadMetadata { archiveTimestamp: number; archived: boolean; autoArchiveDuration: AutoArchiveDuration; - locked?: boolean; + locked: boolean; } // Voice + interface JoinVoiceChannelOptions { + opusOnly?: boolean; + selfDeaf?: boolean; + selfMute?: boolean; + shared?: boolean; + } interface StageInstanceOptions { privacyLevel?: StageInstancePrivacyLevel; topic?: string; } + interface UncachedMemberVoiceState { + id: string; + voiceState: OldVoiceState; + } interface VoiceConnectData { channel_id: string; endpoint: string; @@ -1239,6 +1279,10 @@ declare namespace Eris { STAGE_INSTANCE_CREATE: 83; STAGE_INSTANCE_UPDATE: 84; STAGE_INSTANCE_DELETE: 85; + + THREAD_CREATE: 110; + THREAD_UPDATE: 111; + THREAD_DELETE: 112; }; ChannelTypes: { GUILD_TEXT: 0; @@ -1248,9 +1292,9 @@ declare namespace Eris { GUILD_CATEGORY: 4; GUILD_NEWS: 5; GUILD_STORE: 6; - GUILD_NEWS_THREAD: 10, - GUILD_PUBLIC_THREAD: 11, - GUILD_PRIVATE_THREAD: 12, + GUILD_NEWS_THREAD: 10; + GUILD_PUBLIC_THREAD: 11; + GUILD_PRIVATE_THREAD: 12; GUILD_STAGE: 13; }; GATEWAY_VERSION: 9; @@ -1379,11 +1423,15 @@ declare namespace Eris { /** @deprecated */ useSlashCommands: 2147483648n; voiceRequestToSpeak: 4294967296n; + manageThreads: 17179869184n; + createPublicThreads: 34359738368n; + createPrivateThreads: 68719476736n; useExternalStickers: 137438953472n; + sendMessagesInThreads: 274877906944n; allGuild: 2080899262n; - allText: 140392266833n; + allText: 535529258065n; allVoice: 4629464849n; - all: 146028888063n; + all: 541165879295n; }; REST_VERSION: 9; StickerFormats: { @@ -1799,7 +1847,7 @@ declare namespace Eris { editGuildVoiceState(guildID: string, options: VoiceStateOptions, userID?: string): Promise; editGuildWelcomeScreen(guildID: string, options: WelcomeScreenOptions): Promise; editGuildWidget(guildID: string, options: Widget): Promise; - editMessage(channelID: string, messageID: string, content: MessageContent): Promise; + editMessage(channelID: string, messageID: string, content: MessageContentEdit): Promise; /** @deprecated */ editNickname(guildID: string, nick: string, reason?: string): Promise; editRole(guildID: string, roleID: string, options: RoleOptions, reason?: string): Promise; // TODO not all options are available? @@ -1836,9 +1884,11 @@ declare namespace Eris { executeWebhook(webhookID: string, token: string, options: WebhookPayload & { wait: true }): Promise>; executeWebhook(webhookID: string, token: string, options: WebhookPayload): Promise; followChannel(channelID: string, webhookChannelID: string): Promise; - getActiveThreads(channelID: string): Promise; - getArchivedThreads(channelID: string, type: "private", options?: GetArchivedThreadsOptions): Promise>; - getArchivedThreads(channelID: string, type: "public", options?: GetArchivedThreadsOptions): Promise>; + getActiveGuildThreads(guildID: string): Promise; + /** @deprecated */ + getActiveThreads(channelID: string): Promise; + getArchivedThreads(channelID: string, type: "private", options?: GetArchivedThreadsOptions): Promise>; + getArchivedThreads(channelID: string, type: "public", options?: GetArchivedThreadsOptions): Promise>; getBotGateway(): Promise<{ session_start_limit: { max_concurrency: number; remaining: number; reset_after: number; total: number }; shards: number; url: string }>; getChannel(channelID: string): AnyChannel; getChannelInvites(channelID: string): Promise; @@ -1867,7 +1917,7 @@ declare namespace Eris { getGuildWidgetSettings(guildID: string): Promise; getInvite(inviteID: string, withCounts?: false): Promise>; getInvite(inviteID: string, withCounts: true): Promise>; - getJoinedPrivateArchivedThreads(channelID: string, options?: GetArchivedThreadsOptions): Promise>; + getJoinedPrivateArchivedThreads(channelID: string, options?: GetArchivedThreadsOptions): Promise>; getMessage(channelID: string, messageID: string): Promise; getMessageReaction(channelID: string, messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -1934,7 +1984,7 @@ declare namespace Eris { getWebhook(webhookID: string, token?: string): Promise; getWebhookMessage(webhookID: string, token: string, messageID: string): Promise>; joinThread(channelID: string, userID?: string): Promise; - joinVoiceChannel(channelID: string, options?: { opusOnly?: boolean; shared?: boolean }): Promise; + joinVoiceChannel(channelID: string, options?: JoinVoiceChannelOptions): Promise; kickGuildMember(guildID: string, userID: string, reason?: string): Promise; leaveGuild(guildID: string): Promise; leaveThread(channelID: string, userID?: string): Promise; @@ -2223,6 +2273,7 @@ declare namespace Eris { editWidget(options: Widget): Promise; fetchAllMembers(timeout?: number): Promise; fetchMembers(options?: FetchMembersOptions): Promise; + getActiveThreads(): Promise; getAuditLog(options?: GetGuildAuditLogOptions): Promise; /** @deprecated */ getAuditLogs(limit?: number, before?: string, actionType?: number, userID?: string): Promise; @@ -2474,11 +2525,11 @@ declare namespace Eris { editedTimestamp?: number; embeds: Embed[]; flags: number; - guildID: T extends GuildTextable ? string : undefined; + guildID: T extends GuildTextableWithThread ? string : undefined; id: string; interaction: MessageInteraction | null; jumpLink: string; - member: T extends GuildTextable ? Member : null; + member: T extends GuildTextableWithThread ? Member : null; mentionEveryone: boolean; mentions: User[]; messageReference: MessageReference | null; @@ -2487,13 +2538,13 @@ declare namespace Eris { reactions: { [s: string]: { count: number; me: boolean } }; referencedMessage?: Message | null; roleMentions: string[]; + stickerItems?: StickerItems[]; /** @deprecated */ stickers?: Sticker[]; - stickerItems?: StickerItems[]; timestamp: number; tts: boolean; type: number; - webhookID: T extends GuildTextable ? string | undefined : undefined; + webhookID: T extends GuildTextableWithThread ? string | undefined : undefined; constructor(data: BaseData, client: Client); addReaction(reaction: string): Promise; /** @deprecated */ @@ -2534,7 +2585,8 @@ declare namespace Eris { export class NewsThreadChannel extends ThreadChannel { type: 10; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMessage(messageID: string): Promise>; getMessages(options?: GetMessagesOptions): Promise[]>; @@ -2622,6 +2674,27 @@ declare namespace Eris { getPins(): Promise[]>; } + export class PrivateThreadChannel extends ThreadChannel { + threadMetadata: PrivateThreadMetadata; + type: 12; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } + + export class PublicThreadChannel extends ThreadChannel { + type: 10 | 11; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + edit(options: Pick, reason?: string): Promise; + editMessage(messageID: string, content: MessageContentEdit): Promise>; + getMessage(messageID: string): Promise>; + getMessages(options?: GetMessagesOptions): Promise[]>; + getPins(): Promise[]>; + } + export class Relationship extends Base implements Omit { activities: Activity[] | null; clientStatus?: ClientStatus; @@ -2796,16 +2869,16 @@ declare namespace Eris { } export class StageInstance extends Base { - client: Client; channel: StageChannel | Uncached; + client: Client; discoverableDisabled: boolean; guild: Guild | Uncached; privacyLevel: StageInstancePrivacyLevel; topic: string; constructor(data: BaseData, client: Client); - update(data: BaseData): void; delete(): Promise; edit(options: StageInstanceOptions): Promise; + update(data: BaseData): void; } export class StoreChannel extends GuildChannel { @@ -2827,16 +2900,19 @@ declare namespace Eris { addMessageReaction(messageID: string, reaction: string, userID: string): Promise; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; + createThreadWithMessage(messageID: string, options: CreateThreadOptions): Promise; + createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions): Promise; createWebhook(options: { name: string; avatar?: string | null }, reason?: string): Promise; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; edit(options: Omit, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; - getActiveThreads(): Promise; - getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; - getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; + /** @deprecated */ + getActiveThreads(): Promise; + getArchivedThreads(type: "private", options?: GetArchivedThreadsOptions): Promise>; + getArchivedThreads(type: "public", options?: GetArchivedThreadsOptions): Promise>; getInvites(): Promise<(Invite<"withMetadata", TextChannel>)[]>; - getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions): Promise>; + getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions): Promise>; getMessage(messageID: string): Promise>; getMessageReaction(messageID: string, reaction: string, options?: GetMessageReactionOptions): Promise; /** @deprecated */ @@ -2858,23 +2934,27 @@ declare namespace Eris { unsendMessage(messageID: string): Promise; } - export class ThreadChannel extends GuildChannel { + type A = T; + type B = A; + + export class ThreadChannel extends GuildChannel implements ThreadTextable { lastMessageID: string; + lastPinTimestamp?: number; member?: ThreadMember; memberCount: number; members: Collection; messageCount: number; - messages: Collection; + messages: Collection>; ownerID: string; rateLimitPerUser: number; threadMetadata: ThreadMetadata; type: 10 | 11 | 12; constructor(data: BaseData, client: Client, messageLimit?: number); addMessageReaction(messageID: string, reaction: string): Promise; - createMessage(content: MessageContent, file?: MessageFile | MessageFile): Promise>; + createMessage(content: MessageContent, file?: MessageFile | MessageFile[]): Promise>; deleteMessage(messageID: string, reason?: string): Promise; deleteMessages(messageIDs: string[], reason?: string): Promise; - edit(options: Pick, reason?: string): Promise; + edit(options: Pick, reason?: string): Promise; editMessage(messageID: string, content: MessageContentEdit): Promise>; getMembers(): Promise; getMessage(messageID: string): Promise>; @@ -2895,11 +2975,11 @@ declare namespace Eris { export class ThreadMember extends Base { client: Client; - threadID: string; joinTimestamp: number; + threadID: string; constructor(data: BaseData, client: Client); - update(data: BaseData): void; leave(): Promise; + update(data: BaseData): void; } export class UnavailableGuild extends Base { @@ -2944,7 +3024,7 @@ declare namespace Eris { voiceMembers: Collection; createInvite(options?: CreateInviteOptions, reason?: string): Promise>; getInvites(): Promise<(Invite<"withMetadata", VoiceChannel>)[]>; - join(options: { opusOnly?: boolean; shared?: boolean }): Promise; + join(options?: JoinVoiceChannelOptions): Promise; leave(): void; } diff --git a/lib/Client.js b/lib/Client.js index 016d5cbca..c2cfc8d05 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -645,6 +645,7 @@ class Client extends EventEmitter { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object | Array} [file] A file object (or an Array of them) * @arg {Buffer} file.file A buffer containing file data @@ -659,13 +660,12 @@ class Client extends EventEmitter { }; } else if(content.content !== undefined && typeof content.content !== "string") { content.content = "" + content.content; - } else if(content.content === undefined && !content.embed && !content.embeds && !file) { - return Promise.reject(new Error("No content, file, or embeds")); } else if(content.embed && !content.embeds) { this.emit("warn", "[DEPRECATED] content.embed is deprecated. Use content.embeds instead"); content.embeds = [content.embed]; } content.allowed_mentions = this._formatAllowedMentions(content.allowedMentions); + content.sticker_ids = content.stickerIDs; if(content.messageReference) { content.message_reference = content.messageReference; if(content.messageReference.messageID !== undefined) { @@ -688,8 +688,6 @@ class Client extends EventEmitter { this.emit("warn", "[DEPRECATED] content.messageReferenceID is deprecated. Use content.messageReference instead"); content.message_reference = {message_id: content.messageReferenceID}; } - } else if(!file) { - return Promise.reject(new Error("No content, file, or embeds")); } return this.requestHandler.request("POST", Endpoints.CHANNEL_MESSAGES(channelID), true, content, file).then((message) => new Message(message, this)); } @@ -764,13 +762,15 @@ class Client extends EventEmitter { * @arg {String} channelID The ID of the channel * @arg {Object} options The thread options * @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 + * @arg {boolean} [options.invitable] Whether non-moderators can add other non-moderators to the thread (private threads only) * @arg {String} options.name The thread channel name - * @arg {Number} [options.type] The channel type of the thread to create. It is recommended by Discord to explicitly set this parameter as the default is highly likely to change in a future API version + * @arg {Number} [options.type] The channel type of the thread to create. It is recommended to explicitly set this property as this will be a required property in API v10 * @returns {Promise} */ createThreadWithoutMessage(channelID, options) { return this.requestHandler.request("POST", Endpoints.THREAD_WITHOUT_MESSAGE(channelID), true, { auto_archive_duration: options.autoArchiveDuration, + invitable: options.invitable, name: options.name, type: options.type }).then((channel) => Channel.from(channel, this)); @@ -1041,8 +1041,9 @@ class Client extends EventEmitter { * @arg {Boolean} [options.archived] The archive status of the channel (thread channels only) * @arg {Number} [options.autoArchiveDuration] The duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 (thread channels only) * @arg {Number} [options.bitrate] The bitrate of the channel (guild voice channels only) - * @arg {Number?} [options.defaultArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Number?} [options.defaultAutoArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) * @arg {String} [options.icon] The icon of the channel as a base64 data URI (group channels only). Note: base64 strings alone are not base64 data URI strings + * @arg {Boolean} [options.invitable] Whether non-moderators can add other non-moderators to the channel (private thread channels only) * @arg {Boolean} [options.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel * @arg {Boolean} [options.nsfw] The nsfw status of the channel (guild channels only) @@ -1061,8 +1062,9 @@ class Client extends EventEmitter { archived: options.archived, auto_archive_duration: options.autoArchiveDuration, bitrate: options.bitrate, - default_archive_duration: options.defaultArchiveDuration, + default_auto_archive_duration: options.defaultAutoArchiveDuration, icon: options.icon, + invitable: options.invitable, locked: options.locked, name: options.name, nsfw: options.nsfw, @@ -1386,8 +1388,6 @@ class Client extends EventEmitter { }; } else if(content.content !== undefined && typeof content.content !== "string") { content.content = "" + content.content; - } else if(content.content === undefined && !content.embed && !content.embeds && !content.components && content.flags === undefined) { - return Promise.reject(new Error("No content, embeds, components or flags")); } else if(content.embed && !content.embeds) { this.emit("warn", "[DEPRECATED] content.embed is deprecated. Use content.embeds instead"); content.embeds = [content.embed]; @@ -1666,9 +1666,6 @@ class Client extends EventEmitter { * @returns {Promise} */ editWebhookMessage(webhookID, token, messageID, options) { - if(!options.content && !options.embeds && !options.components && !options.file) { - return Promise.reject(new Error("No content, embed, components, or file")); - } if(options.allowedMentions) { options.allowed_mentions = this._formatAllowedMentions(options.allowedMentions); } @@ -1752,16 +1749,13 @@ class Client extends EventEmitter { * @arg {Object | Array} [options.file] A file object (or an Array of them) * @arg {Buffer} options.file.file A buffer containing file data * @arg {String} options.file.name What to name the file - * @arg {Number} [options.flags] Flags to execute the webhook with, 64 for ephemeral (Interaction webhooks only) + * @arg {String} [options.threadID] The ID of the thread channel in the webhook's channel to send the message to * @arg {Boolean} [options.tts=false] Whether the message should be a TTS message or not * @arg {String} [options.username] A custom username, defaults to webhook default username if not specified * @arg {Boolean} [options.wait=false] Whether to wait for the server to confirm the message create or not * @returns {Promise} */ executeWebhook(webhookID, token, options) { - if(!options.content && !options.file && !options.embeds) { - return Promise.reject(new Error("No content, embeds, or file")); - } let qs = ""; if(options.wait) { qs += "&wait=true"; @@ -1792,12 +1786,32 @@ class Client extends EventEmitter { } /** - * Get all active threads in a channel + * Get all active threads in a guild + * @arg {String} guildID The ID of the guild + * @returns {Promise} An object containing an array of `threads` and an array of `members` + */ + getActiveGuildThreads(guildID) { + return this.requestHandler.request("GET", Endpoints.THREADS_GUILD_ACTIVE(guildID), true).then((response) => { + return { + members: response.members.map((member) => new ThreadMember(member, this)), + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); + } + + /** + * [DEPRECATED] Get all active threads in a channel. Use getActiveGuildThreads instead * @arg {String} channelID The ID of the channel - * @returns {Promise>} + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getActiveThreads(channelID) { - return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((channels) => channels.map((c) => Channel.from(c, this))); + return this.requestHandler.request("GET", Endpoints.THREADS_ACTIVE(channelID), true).then((response) => { + return { + hasMore: response.has_more, + members: response.members.map((member) => new ThreadMember(member, this)), + threads: response.threads.map((thread) => Channel.from(thread, this)) + }; + }); } /** @@ -1807,7 +1821,7 @@ class Client extends EventEmitter { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getArchivedThreads(channelID, type, options = {}) { return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED(channelID, type), true, options).then((response) => { @@ -1915,7 +1929,7 @@ class Client extends EventEmitter { * @arg {String} [options.before] Get entries before this entry ID * @arg {Number} [options.limit=50] The maximum number of entries to return * @arg {String} [options.userID] Filter entries by the user that performed the action - * @returns {Promise<{users: User[], entries: GuildAuditLogEntry[], integrations: PartialIntegration[], webhooks: Webhook[]}>} + * @returns {Promise<{entries: GuildAuditLogEntry[], integrations: PartialIntegration[], threads: (NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel)[], users: User[], webhooks: Webhook[]}>} */ getGuildAuditLog(guildID, options = {}, before, actionType, userID) { if(!options || typeof options !== "object") { @@ -1944,9 +1958,10 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.GUILD_AUDIT_LOGS(guildID), true, options).then((data) => { const guild = this.guilds.get(guildID); return { - users: data.users.map((user) => this.users.add(user, this)), entries: data.audit_log_entries.map((entry) => new GuildAuditLogEntry(entry, guild)), integrations: data.integrations.map((integration) => new GuildIntegration(integration, guild)), + threads: data.threads.map((thread) => guild.threads.update(thread, this)), + users: data.users.map((user) => this.users.add(user, this)), webhooks: data.webhooks }; }); @@ -2110,7 +2125,7 @@ class Client extends EventEmitter { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getJoinedPrivateArchivedThreads(channelID, options = {}) { return this.requestHandler.request("GET", Endpoints.THREADS_ARCHIVED_JOINED(channelID), true, options).then((response) => { @@ -2547,6 +2562,8 @@ class Client extends EventEmitter { * @arg {Object} [options] VoiceConnection constructor options * @arg {Object} [options.opusOnly] Skip opus encoder initialization. You should not enable this unless you know what you are doing * @arg {Object} [options.shared] Whether the VoiceConnection will be part of a SharedStream or not + * @arg {Boolean} [options.selfMute] Whether the bot joins the channel muted or not + * @arg {Boolean} [options.selfDeaf] Whether the bot joins the channel deafened or not * @returns {Promise} Resolves with a VoiceConnection */ joinVoiceChannel(channelID, options = {}) { @@ -2560,8 +2577,8 @@ class Client extends EventEmitter { this.shards.get(this.guildShardMap[this.channelGuildMap[channelID]] || 0).sendWS(Constants.GatewayOPCodes.VOICE_STATE_UPDATE, { guild_id: this.channelGuildMap[channelID] || null, channel_id: channelID || null, - self_mute: false, - self_deaf: false + self_mute: options.selfMute || false, + self_deaf: options.selfDeaf || false }); if(options.opusOnly === undefined) { options.opusOnly = this.options.opusOnly; diff --git a/lib/Constants.js b/lib/Constants.js index 77f4409bc..f6ecd5256 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -67,7 +67,12 @@ const Permissions = { manageEmojisAndStickers: 1n << 30n, manageEmojis: 1n << 30n, // [DEPRECATED] useApplicationCommands: 1n << 31n, useSlashCommands: 1n << 31n, // [DEPRECATED] voiceRequestToSpeak: 1n << 32n, - useExternalStickers: 1n << 37n + + manageThreads: 1n << 34n, + createPublicThreads: 1n << 35n, + createPrivateThreads: 1n << 36n, + useExternalStickers: 1n << 37n, + sendMessagesInThreads: 1n << 38n }; Permissions.allGuild = Permissions.kickMembers | Permissions.banMembers @@ -96,7 +101,11 @@ Permissions.allText = Permissions.createInstantInvite | Permissions.manageRoles | Permissions.manageWebhooks | Permissions.useApplicationCommands - | Permissions.useExternalStickers; + | Permissions.manageThreads + | Permissions.createPublicThreads + | Permissions.createPrivateThreads + | Permissions.useExternalStickers + | Permissions.sendMessagesInThreads; Permissions.allVoice = Permissions.createInstantInvite | Permissions.manageChannels | Permissions.voicePrioritySpeaker @@ -189,7 +198,11 @@ module.exports.AuditLogActions = { INTEGRATION_DELETE: 82, STAGE_INSTANCE_CREATE: 83, STAGE_INSTANCE_UPDATE: 84, - STAGE_INSTANCE_DELETE: 85 + STAGE_INSTANCE_DELETE: 85, + + THREAD_CREATE: 110, + THREAD_UPDATE: 111, + THREAD_DELETE: 112 }; module.exports.MessageActivityTypes = { diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 4611161d0..5fcb5aa58 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2109,6 +2109,9 @@ class Shard extends EventEmitter { case "THREAD_UPDATE": { const channel = this.client.getChannel(packet.d.id); if(!channel) { + const thread = Channel.from(packet.d, this.client); + this.emit("threadUpdate", this.client.guilds.get(packet.d.guild_id).threads.add(thread, this.client), null); + this.client.threadGuildMap[packet.d.id] = packet.d.guild_id; break; } if(!(channel instanceof ThreadChannel)) { @@ -2126,7 +2129,7 @@ class Shard extends EventEmitter { * Fired when a thread channel is updated * @event Client#threadUpdate * @prop {NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel} channel The updated channel - * @prop {Object} oldChannel + * @prop {Object?} oldChannel The old thread channel. This will be null if the channel was uncached * @prop {String} oldChannel.name The name of the channel * @prop {Number} oldChannel.rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled * @prop {Object} oldChannel.threadMetadata Metadata for the thread @@ -2218,7 +2221,22 @@ class Shard extends EventEmitter { removedMembers = packet.d.removed_member_ids.map((id) => channel.members.remove({id}) || {id}); } if(packet.d.added_members) { - addedMembers = packet.d.added_members.map((m) => channel.members.update(m, this.client)); + addedMembers = packet.d.added_members.map((m) => { + if(m.presence) { + m.presence.id = m.presence.user.id; + this.client.users.update(m.presence.user, this.client); + } + + m.thread_id = m.id; + m.id = m.user_id; + m.member.id = m.member.user.id; + const guild = this.client.guilds.get(packet.d.guild_id); + if(guild) { + guild.members.update(m.presence, guild); + guild.members.update(m.member); + } + return channel.members.update(m, this.client); + }); } /** * Fired when anyone is added or removed from a thread. If the `guildMembers` intent is not specified, this will only apply for the current user diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index b29aaa04a..a4d3b6d61 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -80,6 +80,7 @@ module.exports.THREAD_WITHOUT_MESSAGE = (channelID) module.exports.THREADS_ACTIVE = (channelID) => `/channels/${channelID}/threads/active`; module.exports.THREADS_ARCHIVED = (channelID, type) => `/channels/${channelID}/threads/archived/${type}`; module.exports.THREADS_ARCHIVED_JOINED = (channelID) => `/channels/${channelID}/users/@me/threads/archived/private`; +module.exports.THREADS_GUILD_ACTIVE = (guildID) => `/guilds/${guildID}/threads/active`; module.exports.USER = (userID) => `/users/${userID}`; module.exports.USER_BILLING = (userID) => `/users/${userID}/billing`; module.exports.USER_BILLING_PAYMENTS = (userID) => `/users/${userID}/billing/payments`; diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 25bf5d88a..3a4f9061f 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -697,6 +697,14 @@ class Guild extends Base { return this.shard.requestGuildMembers(this.id, options); } + /** + * Get all active threads in this guild + * @returns {Promise} An object containing an array of `threads` and an array of `members` + */ + getActiveThreads() { + return this.client.getActiveThreads.call(this.client, this.id); + } + /** * Get the audit log for the guild * @arg {Object} [options] Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit` diff --git a/lib/structures/GuildAuditLogEntry.js b/lib/structures/GuildAuditLogEntry.js index 4ca2ac524..9dace5570 100644 --- a/lib/structures/GuildAuditLogEntry.js +++ b/lib/structures/GuildAuditLogEntry.js @@ -11,7 +11,7 @@ const {AuditLogActions} = require("../Constants"); * For example, if a channel was renamed from #general to #potato, this would be `{name: "potato"}`` * @prop {Object?} before The properties of the targeted object before the action was taken * For example, if a channel was renamed from #general to #potato, this would be `{name: "general"}`` -* @prop {(CategoryChannel | TextChannel | VoiceChannel | StageChannel)?} channel The channel targeted in the entry, action types 26 (MEMBER_MOVE), 72/74/75 (MESSAGE_DELETE/PIN/UNPIN), and 83-85 (STAGE\_INSTANCE\_CREATE/UPDATE/DELETE) only +* @prop {(CategoryChannel | TextChannel | VoiceChannel | NewsThreadChannel | PrivateThreadChannel | PublicThreadChannel | StageChannel)?} channel The channel targeted in the entry, action types 26 (MEMBER_MOVE), 72/74/75 (MESSAGE_DELETE/PIN/UNPIN) and 83/84/85 (STAGE_INSTANCE_CREATE/UPDATE/DELETE) only * @prop {Number?} count The number of entities targeted * For example, for action type 26 (MEMBER_MOVE), this is the number of members that were moved/disconnected from the voice channel * @prop {Number?} deleteMemberDays The number of days of inactivity to prune for, action type 21 (MEMBER_PRUNE) only @@ -123,7 +123,9 @@ class GuildAuditLogEntry extends Base { return this.guild && this.guild.emojis.find((emoji) => emoji.id === this.targetID); } else if(this.actionType < 80) { // Message return this.guild && this.guild.shard.client.users.get(this.targetID); - } else if(this.actionType < 90) { // Integrations/Stage instances + } else if(this.actionType < 83) { // Integrations + return null; + } else if(this.actionType < 90) { // Stage Instances return this.guild && this.guild.threads.get(this.targetID); } else { throw new Error("Unrecognized action type: " + this.actionType); diff --git a/lib/structures/GuildChannel.js b/lib/structures/GuildChannel.js index 51acb7ad9..b0f3052d1 100644 --- a/lib/structures/GuildChannel.js +++ b/lib/structures/GuildChannel.js @@ -13,7 +13,7 @@ const PermissionOverwrite = require("./PermissionOverwrite"); * @prop {String} id The ID of the channel * @prop {String} name The name of the channel * @prop {Boolean} nsfw Whether the channel is an NSFW channel or not -* @prop {String?} parentID The ID of the category this channel belongs to or the message ID where the thread originated from (thread channels only) +* @prop {String?} parentID The ID of the category this channel belongs to or the channel ID where the thread originated from (thread channels only) * @prop {Collection} permissionOverwrites Collection of PermissionOverwrites in this channel * @prop {Number} position The position of the channel */ @@ -73,7 +73,8 @@ class GuildChannel extends Channel { * @arg {Object} options The properties to edit * @arg {Boolean} [options.archived] The archive status of the channel (thread channels only) * @arg {Number} [options.autoArchiveDuration] The duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 (thread channels only) - * @arg {Number?} [options.defaultArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Number?} [options.defaultAutoArchiveDuration] The default duration of newly created threads in minutes to automatically archive the thread after inactivity (60, 1440, 4320, 10080) (guild text/news channels only) + * @arg {Boolean} [options.invitable] Whether non-moderators can add other non-moderators to the channel (private thread channels only) * @arg {Boolean} [options.locked] The lock status of the channel (thread channels only) * @arg {String} [options.name] The name of the channel * @arg {String} [options.topic] The topic of the channel (guild text channels only) @@ -83,7 +84,7 @@ class GuildChannel extends Channel { * @arg {Number} [options.rateLimitPerUser] The time in seconds a user has to wait before sending another message (does not affect bots or users with manageMessages/manageChannel permissions) (guild text and thread channels only) * @arg {String?} [options.rtcRegion] The RTC region ID of the channel (automatic if `null`) (guild voice channels only) * @arg {Boolean} [options.nsfw] The nsfw status of the channel - * @arg {Number?} [options.parentID] The ID of the parent channel category for this channel (guild text/voice channels only) or the message ID where the thread originated from (thread channels only) + * @arg {Number?} [options.parentID] The ID of the parent channel category for this channel (guild text/voice channels only) or the channel ID where the thread originated from (thread channels only) * @arg {String} [reason] The reason to be displayed in audit logs * @returns {Promise} */ @@ -127,20 +128,21 @@ class GuildChannel extends Channel { if(permission & Permissions.administrator) { return new Permission(Permissions.all); } - let overwrite = this.permissionOverwrites.get(this.guild.id); + const channel = this instanceof ThreadChannel ? this.guild.channels.get(this.parentID) : this; + let overwrite = channel && channel.permissionOverwrites.get(this.guild.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } let deny = 0n; let allow = 0n; for(const roleID of member.roles) { - if((overwrite = this.permissionOverwrites.get(roleID))) { + if((overwrite = channel && channel.permissionOverwrites.get(roleID))) { deny |= overwrite.deny; allow |= overwrite.allow; } } permission = (permission & ~deny) | allow; - overwrite = this.permissionOverwrites.get(member.id); + overwrite = channel && channel.permissionOverwrites.get(member.id); if(overwrite) { permission = (permission & ~overwrite.deny) | overwrite.allow; } @@ -160,3 +162,5 @@ class GuildChannel extends Channel { } module.exports = GuildChannel; + +const ThreadChannel = require("./ThreadChannel"); diff --git a/lib/structures/Invite.js b/lib/structures/Invite.js index 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 diff --git a/lib/structures/Message.js b/lib/structures/Message.js index 886122fa4..b427d91a9 100644 --- a/lib/structures/Message.js +++ b/lib/structures/Message.js @@ -434,23 +434,23 @@ class Message extends Base { /** * Edit the message * @arg {String | Array | Object} content A string, array of strings, or object. If an object is passed: - * @arg {Array} [options.components] An array of component objects - * @arg {String} [options.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only) - * @arg {Boolean} [options.components[].disabled] Whether the component is disabled (type 2 only) - * @arg {Object} [options.components[].emoji] The emoji to be displayed in the component (type 2) - * @arg {String} [options.components[].label] The label to be displayed in the component (type 2) + * @arg {Array} [content.components] An array of component objects + * @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only) + * @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 only) + * @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2) + * @arg {String} [content.components[].label] The label to be displayed in the component (type 2) * @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1) * @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1) - * @arg {Array} [options.components[].options] The options for this component (type 3 only) - * @arg {Boolean} [options.components[].options[].default] Whether this option should be the default value selected - * @arg {String} [options.components[].options[].description] The description for this option - * @arg {Object} [options.components[].options[].emoji] The emoji to be displayed in this option - * @arg {String} options.components[].options[].label The label for this option - * @arg {Number | String} options.components[].options[].value The value for this option - * @arg {String} [options.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only) - * @arg {Number} [options.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required - * @arg {Number} options.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu - * @arg {String} [options.components[].url] The URL that the component should open for users (type 2 style 5 only) + * @arg {Array} [content.components[].options] The options for this component (type 3 only) + * @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected + * @arg {String} [content.components[].options[].description] The description for this option + * @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option + * @arg {String} content.components[].options[].label The label for this option + * @arg {Number | String} content.components[].options[].value The value for this option + * @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only) + * @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required + * @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu + * @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only) * @arg {String} content.content A content string * @arg {Boolean} [content.disableEveryone] Whether to filter @everyone/@here or not (overrides default) * @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure diff --git a/lib/structures/PrivateChannel.js b/lib/structures/PrivateChannel.js index a4eca21e7..8ae63e89d 100644 --- a/lib/structures/PrivateChannel.js +++ b/lib/structures/PrivateChannel.js @@ -71,6 +71,7 @@ class PrivateChannel extends Channel { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to the stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object} [file] A file object * @arg {Buffer} file.file A buffer containing file data diff --git a/lib/structures/PrivateThreadChannel.js b/lib/structures/PrivateThreadChannel.js index 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 3ac027b9b..d34a339ab 100644 --- a/lib/structures/TextChannel.js +++ b/lib/structures/TextChannel.js @@ -96,6 +96,7 @@ class TextChannel extends GuildChannel { * @arg {String} [content.messageReference.guildID] The guild ID of the referenced message * @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message * @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead + * @arg {Array} [content.stickerIDs] An array of IDs corresponding to the stickers to send * @arg {Boolean} [content.tts] Set the message TTS flag * @arg {Object} [file] A file object * @arg {Buffer} file.file A buffer containing file data @@ -122,8 +123,9 @@ class TextChannel extends GuildChannel { * Create a thread without an existing message * @arg {Object} options The thread options * @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 + * @arg {boolean} [options.invitable] Whether non-moderators can add other non-moderators to the thread (private threads only) * @arg {String} options.name The thread channel name - * @arg {Number} [options.type] The channel type of the thread to create. It is recommended by Discord to explicitly set this parameter as the default is highly likely to change in a future API version + * @arg {Number} [options.type] The channel type of the thread to create. It is recommended to explicitly set this property as this will be a required property in API v10 * @returns {Promise} */ createThreadWithoutMessage(options) { @@ -202,8 +204,8 @@ class TextChannel extends GuildChannel { } /** - * Get all active threads in this channel - * @returns {Promise>} + * [DEPRECATED] Get all active threads in this channel. Use guild.getActiveThreads instead + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getActiveThreads() { return this.client.getActiveThreads.call(this.client, this.id); @@ -215,7 +217,7 @@ class TextChannel extends GuildChannel { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getArchivedThreads(type, options) { return this.client.getArchivedThreads.call(this.client, this.id, type, options); @@ -234,7 +236,7 @@ class TextChannel extends GuildChannel { * @arg {Object} [options] Additional options when requesting archived threads * @arg {Date} [options.before] List of threads to return before the timestamp * @arg {Number} [options.limit] Maximum number of threads to return - * @returns {Object} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call + * @returns {Promise} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call */ getJoinedPrivateArchivedThreads(options) { return this.client.getJoinedPrivateArchivedThreads.call(this.client, this.id, options); diff --git a/lib/structures/ThreadChannel.js b/lib/structures/ThreadChannel.js index 2b3e4c883..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) { @@ -51,7 +51,6 @@ class ThreadChannel extends GuildChannel { this.threadMetadata = { archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp), archived: data.thread_metadata.archived, - archiverID: data.thread_metadata.archiver_id, autoArchiveDuration: data.thread_metadata.auto_archive_duration, locked: data.thread_metadata.locked }; diff --git a/lib/structures/ThreadMember.js b/lib/structures/ThreadMember.js index 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) { diff --git a/lib/structures/VoiceChannel.js b/lib/structures/VoiceChannel.js index 1e68c370a..c9615eaf7 100644 --- a/lib/structures/VoiceChannel.js +++ b/lib/structures/VoiceChannel.js @@ -65,6 +65,8 @@ class VoiceChannel extends GuildChannel { * @arg {Object} [options] VoiceConnection constructor options * @arg {Object} [options.opusOnly] Skip opus encoder initialization. You should not enable this unless you know what you are doing * @arg {Object} [options.shared] Whether the VoiceConnection will be part of a SharedStream or not + * @arg {Boolean} [options.selfMute] Whether the bot joins the channel muted or not + * @arg {Boolean} [options.selfDeaf] Whether the bot joins the channel deafened or not * @returns {Promise} Resolves with a VoiceConnection */ join(options) {