mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 03:53:29 +01:00
feat: add support for guild forums (#7791)
* feat: add support for guild forums * feat(webhook): add support for creating forum channel posts * fix: duplicated docs * feat: add support for message counts * feat: add support for latest upstream changes * fix: serialize forum channels * types: fix channel unions * types: fix tests * types: fix tests (again) * types: fix tests (again (again)) * chore: make requested changes * chore: fix bugs and make requested changes * types: use correct type for guild forum start messages * chore: remove console.log * chore: make requested changes * chore: make requested changes * chore: fix docs * Update packages/discord.js/src/managers/GuildForumThreadManager.js Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * chore: update types * chore: make requested changes * chore: Apply suggestions Co-authored-by: Jaworek <jaworekwiadomosci@gmail.com> Co-authored-by: Jonathan Rubenstein <jrubcop@gmail.com> * fix: import `ErrorCodes` * fix: remove defunct code * refactor: be consistent with channel class names * feat(GuildChannel): add flags * fix: rename file * refactor: channel flags are everywhere! * fix: import flags correctly * chore(ThreadChannel): update message count string * docs(Channels): correct `@param` type * docs(Channels): ignore transformGuildDefaultReaction * refactor: emoji object in tags * chore: renaming consistency * fix: document default reaction emojis in patching * fix(GuildChannelManager): document `defaultThreadRateLimitPerUser` * chore: semicolon * docs(ErrorCodes): document `GuildForumMessageRequired` * refactor: transform default reactions * docs(APITypes): Add `ChannelFlags` * fix: convert tags properly * fix: pass an array of snowflakes * refactor: handle flags better * fix(ThreadChannel): receive tags * fix(PartialGroupDMChannel): nullify `flags` Apparently did not do this earlier. * chore: misc sorting * refactor: nullify emoji on tags if not present * refactor(ForumChannel): modify returns * types: protect the thread manager! Co-authored-by: SpaceEEC <spaceeec@yahoo.com> * chore: update `ChannelType` usage * Update index.d.ts * docs: Update default reaction emoji property names Co-authored-by: Almeida <almeidx@pm.me> * fix: only `name` is required when editing tags - discord/discord-api-docs#5458 * types: add tests for `channel.flags` * fix: allow unsetting the default reaction emoji * refactor: remove v13 remnants * docs: add missing closing tag * feat: add `rateLimitPerUser` * feat: add missing properties for create guild channel - discord/discord-api-docs#5474 * refactor(GuildForumThreadManager): refactor message payload * fix: handle magical `null` case Co-authored-by: A. Román <kyradiscord@gmail.com> Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Jaworek <jaworekwiadomosci@gmail.com> Co-authored-by: Jonathan Rubenstein <jrubcop@gmail.com> Co-authored-by: SpaceEEC <spaceeec@yahoo.com> Co-authored-by: Almeida <almeidx@pm.me> Co-authored-by: A. Román <kyradiscord@gmail.com>
This commit is contained in:
@@ -143,6 +143,8 @@
|
|||||||
|
|
||||||
* @property {'NotImplemented'} NotImplemented
|
* @property {'NotImplemented'} NotImplemented
|
||||||
|
|
||||||
|
* @property {'GuildForumMessageRequired'} GuildForumMessageRequired
|
||||||
|
|
||||||
* @property {'SweepFilterReturn'} SweepFilterReturn
|
* @property {'SweepFilterReturn'} SweepFilterReturn
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -288,6 +290,8 @@ const keys = [
|
|||||||
'NotImplemented',
|
'NotImplemented',
|
||||||
|
|
||||||
'SweepFilterReturn',
|
'SweepFilterReturn',
|
||||||
|
|
||||||
|
'GuildForumMessageRequired',
|
||||||
];
|
];
|
||||||
|
|
||||||
// JSDoc for IntelliSense purposes
|
// JSDoc for IntelliSense purposes
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ const Messages = {
|
|||||||
[DjsErrorCodes.NotImplemented]: (what, name) => `Method ${what} not implemented on ${name}.`,
|
[DjsErrorCodes.NotImplemented]: (what, name) => `Method ${what} not implemented on ${name}.`,
|
||||||
|
|
||||||
[DjsErrorCodes.SweepFilterReturn]: 'The return value of the sweepFilter function was not false or a Function',
|
[DjsErrorCodes.SweepFilterReturn]: 'The return value of the sweepFilter function was not false or a Function',
|
||||||
|
|
||||||
|
[DjsErrorCodes.GuildForumMessageRequired]: 'You must provide a message to create a guild forum thread',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Messages;
|
module.exports = Messages;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ exports.ActivityFlagsBitField = require('./util/ActivityFlagsBitField');
|
|||||||
exports.ApplicationFlagsBitField = require('./util/ApplicationFlagsBitField');
|
exports.ApplicationFlagsBitField = require('./util/ApplicationFlagsBitField');
|
||||||
exports.BaseManager = require('./managers/BaseManager');
|
exports.BaseManager = require('./managers/BaseManager');
|
||||||
exports.BitField = require('./util/BitField');
|
exports.BitField = require('./util/BitField');
|
||||||
|
exports.ChannelFlagsBitField = require('./util/ChannelFlagsBitField');
|
||||||
exports.Collection = require('@discordjs/collection').Collection;
|
exports.Collection = require('@discordjs/collection').Collection;
|
||||||
exports.Constants = require('./util/Constants');
|
exports.Constants = require('./util/Constants');
|
||||||
exports.Colors = require('./util/Colors');
|
exports.Colors = require('./util/Colors');
|
||||||
@@ -58,12 +59,14 @@ exports.GuildBanManager = require('./managers/GuildBanManager');
|
|||||||
exports.GuildChannelManager = require('./managers/GuildChannelManager');
|
exports.GuildChannelManager = require('./managers/GuildChannelManager');
|
||||||
exports.GuildEmojiManager = require('./managers/GuildEmojiManager');
|
exports.GuildEmojiManager = require('./managers/GuildEmojiManager');
|
||||||
exports.GuildEmojiRoleManager = require('./managers/GuildEmojiRoleManager');
|
exports.GuildEmojiRoleManager = require('./managers/GuildEmojiRoleManager');
|
||||||
|
exports.GuildForumThreadManager = require('./managers/GuildForumThreadManager');
|
||||||
exports.GuildInviteManager = require('./managers/GuildInviteManager');
|
exports.GuildInviteManager = require('./managers/GuildInviteManager');
|
||||||
exports.GuildManager = require('./managers/GuildManager');
|
exports.GuildManager = require('./managers/GuildManager');
|
||||||
exports.GuildMemberManager = require('./managers/GuildMemberManager');
|
exports.GuildMemberManager = require('./managers/GuildMemberManager');
|
||||||
exports.GuildMemberRoleManager = require('./managers/GuildMemberRoleManager');
|
exports.GuildMemberRoleManager = require('./managers/GuildMemberRoleManager');
|
||||||
exports.GuildScheduledEventManager = require('./managers/GuildScheduledEventManager');
|
exports.GuildScheduledEventManager = require('./managers/GuildScheduledEventManager');
|
||||||
exports.GuildStickerManager = require('./managers/GuildStickerManager');
|
exports.GuildStickerManager = require('./managers/GuildStickerManager');
|
||||||
|
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
|
||||||
exports.MessageManager = require('./managers/MessageManager');
|
exports.MessageManager = require('./managers/MessageManager');
|
||||||
exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager');
|
exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager');
|
||||||
exports.PresenceManager = require('./managers/PresenceManager');
|
exports.PresenceManager = require('./managers/PresenceManager');
|
||||||
@@ -109,6 +112,7 @@ exports.DMChannel = require('./structures/DMChannel');
|
|||||||
exports.Embed = require('./structures/Embed');
|
exports.Embed = require('./structures/Embed');
|
||||||
exports.EmbedBuilder = require('./structures/EmbedBuilder');
|
exports.EmbedBuilder = require('./structures/EmbedBuilder');
|
||||||
exports.Emoji = require('./structures/Emoji').Emoji;
|
exports.Emoji = require('./structures/Emoji').Emoji;
|
||||||
|
exports.ForumChannel = require('./structures/ForumChannel');
|
||||||
exports.Guild = require('./structures/Guild').Guild;
|
exports.Guild = require('./structures/Guild').Guild;
|
||||||
exports.GuildAuditLogs = require('./structures/GuildAuditLogs');
|
exports.GuildAuditLogs = require('./structures/GuildAuditLogs');
|
||||||
exports.GuildAuditLogsEntry = require('./structures/GuildAuditLogsEntry');
|
exports.GuildAuditLogsEntry = require('./structures/GuildAuditLogsEntry');
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ class CategoryChannelChildManager extends DataManager {
|
|||||||
* @property {number} [rateLimitPerUser] The rate limit per user (slowmode) for the new channel in seconds
|
* @property {number} [rateLimitPerUser] The rate limit per user (slowmode) for the new channel in seconds
|
||||||
* @property {string} [rtcRegion] The specific region of the new channel.
|
* @property {string} [rtcRegion] The specific region of the new channel.
|
||||||
* @property {VideoQualityMode} [videoQualityMode] The camera video quality mode of the voice channel
|
* @property {VideoQualityMode} [videoQualityMode] The camera video quality mode of the voice channel
|
||||||
|
* @property {GuildForumTagData[]} [availableTags] The tags that can be used in this channel (forum only).
|
||||||
|
* @property {DefaultReactionEmoji} [defaultReactionEmoji]
|
||||||
|
* The emoji to show in the add reaction button on a thread in a guild forum channel.
|
||||||
* @property {string} [reason] Reason for creating the new channel
|
* @property {string} [reason] Reason for creating the new channel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ const process = require('node:process');
|
|||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const { ChannelType, Routes } = require('discord-api-types/v10');
|
const { ChannelType, Routes } = require('discord-api-types/v10');
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const ThreadManager = require('./ThreadManager');
|
const GuildTextThreadManager = require('./GuildTextThreadManager');
|
||||||
const { Error, TypeError, ErrorCodes } = require('../errors');
|
const { Error, TypeError, ErrorCodes } = require('../errors');
|
||||||
const GuildChannel = require('../structures/GuildChannel');
|
const GuildChannel = require('../structures/GuildChannel');
|
||||||
const PermissionOverwrites = require('../structures/PermissionOverwrites');
|
const PermissionOverwrites = require('../structures/PermissionOverwrites');
|
||||||
const ThreadChannel = require('../structures/ThreadChannel');
|
const ThreadChannel = require('../structures/ThreadChannel');
|
||||||
const Webhook = require('../structures/Webhook');
|
const Webhook = require('../structures/Webhook');
|
||||||
|
const { transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Channels');
|
||||||
const { ThreadChannelTypes } = require('../util/Constants');
|
const { ThreadChannelTypes } = require('../util/Constants');
|
||||||
const DataResolver = require('../util/DataResolver');
|
const DataResolver = require('../util/DataResolver');
|
||||||
const { setPosition } = require('../util/Util');
|
const { setPosition } = require('../util/Util');
|
||||||
@@ -137,6 +138,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
rateLimitPerUser,
|
rateLimitPerUser,
|
||||||
rtcRegion,
|
rtcRegion,
|
||||||
videoQualityMode,
|
videoQualityMode,
|
||||||
|
availableTags,
|
||||||
|
defaultReactionEmoji,
|
||||||
reason,
|
reason,
|
||||||
}) {
|
}) {
|
||||||
parent &&= this.client.channels.resolveId(parent);
|
parent &&= this.client.channels.resolveId(parent);
|
||||||
@@ -156,6 +159,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
rate_limit_per_user: rateLimitPerUser,
|
rate_limit_per_user: rateLimitPerUser,
|
||||||
rtc_region: rtcRegion,
|
rtc_region: rtcRegion,
|
||||||
video_quality_mode: videoQualityMode,
|
video_quality_mode: videoQualityMode,
|
||||||
|
available_tags: availableTags?.map(availableTag => transformGuildForumTag(availableTag)),
|
||||||
|
default_reaction_emoji: defaultReactionEmoji && transformGuildDefaultReaction(defaultReactionEmoji),
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -218,6 +223,9 @@ class GuildChannelManager extends CachedManager {
|
|||||||
* The default auto archive duration for all new threads in this channel
|
* The default auto archive duration for all new threads in this channel
|
||||||
* @property {?string} [rtcRegion] The RTC region of the channel
|
* @property {?string} [rtcRegion] The RTC region of the channel
|
||||||
* @property {?VideoQualityMode} [videoQualityMode] The camera video quality mode of the channel
|
* @property {?VideoQualityMode} [videoQualityMode] The camera video quality mode of the channel
|
||||||
|
* @property {GuildForumTagData[]} [availableTags] The tags to set as available in a forum channel
|
||||||
|
* @property {?DefaultReactionEmoji} [defaultReactionEmoji] The emoji to set as the default reaction emoji
|
||||||
|
* @property {number} [defaultThreadRateLimitPerUser] The rate limit per user (slowmode) to set on forum posts
|
||||||
* @property {string} [reason] Reason for editing this channel
|
* @property {string} [reason] Reason for editing this channel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -274,6 +282,9 @@ class GuildChannelManager extends CachedManager {
|
|||||||
rate_limit_per_user: data.rateLimitPerUser,
|
rate_limit_per_user: data.rateLimitPerUser,
|
||||||
default_auto_archive_duration: data.defaultAutoArchiveDuration,
|
default_auto_archive_duration: data.defaultAutoArchiveDuration,
|
||||||
permission_overwrites,
|
permission_overwrites,
|
||||||
|
available_tags: data.availableTags?.map(availableTag => transformGuildForumTag(availableTag)),
|
||||||
|
default_reaction_emoji: data.defaultReactionEmoji && transformGuildDefaultReaction(data.defaultReactionEmoji),
|
||||||
|
default_thread_rate_limit_per_user: data.defaultThreadRateLimitPerUser,
|
||||||
},
|
},
|
||||||
reason: data.reason,
|
reason: data.reason,
|
||||||
});
|
});
|
||||||
@@ -418,7 +429,7 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async fetchActiveThreads(cache = true) {
|
async fetchActiveThreads(cache = true) {
|
||||||
const raw = await this.client.rest.get(Routes.guildActiveThreads(this.guild.id));
|
const raw = await this.client.rest.get(Routes.guildActiveThreads(this.guild.id));
|
||||||
return ThreadManager._mapThreads(raw, this.client, { guild: this.guild, cache });
|
return GuildTextThreadManager._mapThreads(raw, this.client, { guild: this.guild, cache });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
76
packages/discord.js/src/managers/GuildForumThreadManager.js
Normal file
76
packages/discord.js/src/managers/GuildForumThreadManager.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Routes } = require('discord-api-types/v10');
|
||||||
|
const ThreadManager = require('./ThreadManager');
|
||||||
|
const { TypeError, ErrorCodes } = require('../errors');
|
||||||
|
const MessagePayload = require('../structures/MessagePayload');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages API methods for threads in forum channels and stores their cache.
|
||||||
|
* @extends {ThreadManager}
|
||||||
|
*/
|
||||||
|
class GuildForumThreadManager extends ThreadManager {
|
||||||
|
/**
|
||||||
|
* @typedef {BaseMessageOptions} GuildForumThreadCreateOptions
|
||||||
|
* @property {stickers} [stickers] The stickers to send with the message
|
||||||
|
* @property {BitFieldResolvable} [flags] The flags to send with the message
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for creating a thread.
|
||||||
|
* @typedef {StartThreadOptions} GuildForumThreadCreateOptions
|
||||||
|
* @property {GuildForumThreadCreateOptions|MessagePayload} message The message associated with the thread post
|
||||||
|
* @property {Snowflake[]} [appliedTags] The tags to apply to the thread
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new thread in the channel.
|
||||||
|
* @param {GuildForumThreadCreateOptions} [options] Options to create a new thread
|
||||||
|
* @returns {Promise<ThreadChannel>}
|
||||||
|
* @example
|
||||||
|
* // Create a new forum post
|
||||||
|
* forum.threads
|
||||||
|
* .create({
|
||||||
|
* name: 'Food Talk',
|
||||||
|
* autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||||
|
* message: {
|
||||||
|
* content: 'Discuss your favorite food!',
|
||||||
|
* },
|
||||||
|
* reason: 'Needed a separate thread for food',
|
||||||
|
* })
|
||||||
|
* .then(threadChannel => console.log(threadChannel))
|
||||||
|
* .catch(console.error);
|
||||||
|
*/
|
||||||
|
async create({
|
||||||
|
name,
|
||||||
|
autoArchiveDuration = this.channel.defaultAutoArchiveDuration,
|
||||||
|
message,
|
||||||
|
reason,
|
||||||
|
rateLimitPerUser,
|
||||||
|
appliedTags,
|
||||||
|
} = {}) {
|
||||||
|
if (!message) {
|
||||||
|
throw new TypeError(ErrorCodes.GuildForumMessageRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body, files } = await (message instanceof MessagePayload ? message : MessagePayload.create(this, message))
|
||||||
|
.resolveBody()
|
||||||
|
.resolveFiles();
|
||||||
|
|
||||||
|
const data = await this.client.rest.post(Routes.threads(this.channel.id), {
|
||||||
|
body: {
|
||||||
|
name,
|
||||||
|
auto_archive_duration: autoArchiveDuration,
|
||||||
|
rate_limit_per_user: rateLimitPerUser,
|
||||||
|
applied_tags: appliedTags,
|
||||||
|
message: body,
|
||||||
|
},
|
||||||
|
files,
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.client.actions.ThreadCreate.handle(data).thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GuildForumThreadManager;
|
||||||
85
packages/discord.js/src/managers/GuildTextThreadManager.js
Normal file
85
packages/discord.js/src/managers/GuildTextThreadManager.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { ChannelType, Routes } = require('discord-api-types/v10');
|
||||||
|
const ThreadManager = require('./ThreadManager');
|
||||||
|
const { ErrorCodes, TypeError } = require('../errors');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages API methods for {@link ThreadChannel} objects and stores their cache.
|
||||||
|
* @extends {CachedManager}
|
||||||
|
*/
|
||||||
|
class GuildTextThreadManager extends ThreadManager {
|
||||||
|
/**
|
||||||
|
* Options for creating a thread. <warn>Only one of `startMessage` or `type` can be defined.</warn>
|
||||||
|
* @typedef {StartThreadOptions} ThreadCreateOptions
|
||||||
|
* @property {MessageResolvable} [startMessage] The message to start a thread from.
|
||||||
|
* <warn>If this is defined, then the `type` of thread gets inferred automatically and cannot be changed.</warn>
|
||||||
|
* @property {ThreadChannelTypes} [type] The type of thread to create.
|
||||||
|
* Defaults to {@link ChannelType.PublicThread} if created in a {@link TextChannel}
|
||||||
|
* <warn>When creating threads in a {@link NewsChannel}, this is ignored and is always
|
||||||
|
* {@link ChannelType.AnnouncementThread}</warn>
|
||||||
|
* @property {boolean} [invitable] Whether non-moderators can add other non-moderators to the thread
|
||||||
|
* <info>Can only be set when type will be {@link ChannelType.PrivateThread}</info>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new thread in the channel.
|
||||||
|
* @param {ThreadCreateOptions} [options] Options to create a new thread
|
||||||
|
* @returns {Promise<ThreadChannel>}
|
||||||
|
* @example
|
||||||
|
* // Create a new public thread
|
||||||
|
* channel.threads
|
||||||
|
* .create({
|
||||||
|
* name: 'food-talk',
|
||||||
|
* autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||||
|
* reason: 'Needed a separate thread for food',
|
||||||
|
* })
|
||||||
|
* .then(threadChannel => console.log(threadChannel))
|
||||||
|
* .catch(console.error);
|
||||||
|
* @example
|
||||||
|
* // Create a new private thread
|
||||||
|
* channel.threads
|
||||||
|
* .create({
|
||||||
|
* name: 'mod-talk',
|
||||||
|
* autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||||
|
* type: ChannelType.PrivateThread,
|
||||||
|
* reason: 'Needed a separate thread for moderation',
|
||||||
|
* })
|
||||||
|
* .then(threadChannel => console.log(threadChannel))
|
||||||
|
* .catch(console.error);
|
||||||
|
*/
|
||||||
|
async create({
|
||||||
|
name,
|
||||||
|
autoArchiveDuration = this.channel.defaultAutoArchiveDuration,
|
||||||
|
startMessage,
|
||||||
|
type,
|
||||||
|
invitable,
|
||||||
|
reason,
|
||||||
|
rateLimitPerUser,
|
||||||
|
} = {}) {
|
||||||
|
let resolvedType =
|
||||||
|
this.channel.type === ChannelType.GuildAnnouncement ? ChannelType.AnnouncementThread : ChannelType.PublicThread;
|
||||||
|
let startMessageId;
|
||||||
|
if (startMessage) {
|
||||||
|
startMessageId = this.channel.messages.resolveId(startMessage);
|
||||||
|
if (!startMessageId) throw new TypeError(ErrorCodes.InvalidType, 'startMessage', 'MessageResolvable');
|
||||||
|
} else if (this.channel.type !== ChannelType.GuildAnnouncement) {
|
||||||
|
resolvedType = type ?? resolvedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await this.client.rest.post(Routes.threads(this.channel.id, startMessageId), {
|
||||||
|
body: {
|
||||||
|
name,
|
||||||
|
auto_archive_duration: autoArchiveDuration,
|
||||||
|
type: resolvedType,
|
||||||
|
invitable: resolvedType === ChannelType.PrivateThread ? invitable : undefined,
|
||||||
|
rate_limit_per_user: rateLimitPerUser,
|
||||||
|
},
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.client.actions.ThreadCreate.handle(data).thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GuildTextThreadManager;
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const { makeURLSearchParams } = require('@discordjs/rest');
|
const { makeURLSearchParams } = require('@discordjs/rest');
|
||||||
const { ChannelType, Routes } = require('discord-api-types/v10');
|
const { Routes } = require('discord-api-types/v10');
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { TypeError, ErrorCodes } = require('../errors');
|
const { TypeError, ErrorCodes } = require('../errors');
|
||||||
const ThreadChannel = require('../structures/ThreadChannel');
|
const ThreadChannel = require('../structures/ThreadChannel');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages API methods for {@link ThreadChannel} objects and stores their cache.
|
* Manages API methods for thread-based channels and stores their cache.
|
||||||
* @extends {CachedManager}
|
* @extends {CachedManager}
|
||||||
*/
|
*/
|
||||||
class ThreadManager extends CachedManager {
|
class ThreadManager extends CachedManager {
|
||||||
@@ -17,11 +17,18 @@ class ThreadManager extends CachedManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The channel this Manager belongs to
|
* The channel this Manager belongs to
|
||||||
* @type {NewsChannel|TextChannel}
|
* @type {NewsChannel|TextChannel|ForumChannel}
|
||||||
*/
|
*/
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data that can be resolved to a Thread Channel object. This can be:
|
||||||
|
* * A ThreadChannel object
|
||||||
|
* * A Snowflake
|
||||||
|
* @typedef {ThreadChannel|Snowflake} ThreadChannelResolvable
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cache of this Manager
|
* The cache of this Manager
|
||||||
* @type {Collection<Snowflake, ThreadChannel>}
|
* @type {Collection<Snowflake, ThreadChannel>}
|
||||||
@@ -35,13 +42,6 @@ class ThreadManager extends CachedManager {
|
|||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Data that can be resolved to a Thread Channel object. This can be:
|
|
||||||
* * A ThreadChannel object
|
|
||||||
* * A Snowflake
|
|
||||||
* @typedef {ThreadChannel|Snowflake} ThreadChannelResolvable
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a {@link ThreadChannelResolvable} to a {@link ThreadChannel} object.
|
* Resolves a {@link ThreadChannelResolvable} to a {@link ThreadChannel} object.
|
||||||
* @method resolve
|
* @method resolve
|
||||||
@@ -74,68 +74,6 @@ class ThreadManager extends CachedManager {
|
|||||||
* <info>Can only be set when type will be {@link ChannelType.PrivateThread}</info>
|
* <info>Can only be set when type will be {@link ChannelType.PrivateThread}</info>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new thread in the channel.
|
|
||||||
* @param {ThreadCreateOptions} [options] Options to create a new thread
|
|
||||||
* @returns {Promise<ThreadChannel>}
|
|
||||||
* @example
|
|
||||||
* // Create a new public thread
|
|
||||||
* channel.threads
|
|
||||||
* .create({
|
|
||||||
* name: 'food-talk',
|
|
||||||
* autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
|
||||||
* reason: 'Needed a separate thread for food',
|
|
||||||
* })
|
|
||||||
* .then(threadChannel => console.log(threadChannel))
|
|
||||||
* .catch(console.error);
|
|
||||||
* @example
|
|
||||||
* // Create a new private thread
|
|
||||||
* channel.threads
|
|
||||||
* .create({
|
|
||||||
* name: 'mod-talk',
|
|
||||||
* autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
|
||||||
* type: ChannelType.PrivateThread,
|
|
||||||
* reason: 'Needed a separate thread for moderation',
|
|
||||||
* })
|
|
||||||
* .then(threadChannel => console.log(threadChannel))
|
|
||||||
* .catch(console.error);
|
|
||||||
*/
|
|
||||||
async create({
|
|
||||||
name,
|
|
||||||
autoArchiveDuration = this.channel.defaultAutoArchiveDuration,
|
|
||||||
startMessage,
|
|
||||||
type,
|
|
||||||
invitable,
|
|
||||||
reason,
|
|
||||||
rateLimitPerUser,
|
|
||||||
} = {}) {
|
|
||||||
if (type && typeof type !== 'string' && typeof type !== 'number') {
|
|
||||||
throw new TypeError(ErrorCodes.InvalidType, 'type', 'ThreadChannelType or Number');
|
|
||||||
}
|
|
||||||
let resolvedType =
|
|
||||||
this.channel.type === ChannelType.GuildAnnouncement ? ChannelType.AnnouncementThread : ChannelType.PublicThread;
|
|
||||||
let startMessageId;
|
|
||||||
if (startMessage) {
|
|
||||||
startMessageId = this.channel.messages.resolveId(startMessage);
|
|
||||||
if (!startMessageId) throw new TypeError(ErrorCodes.InvalidType, 'startMessage', 'MessageResolvable');
|
|
||||||
} else if (this.channel.type !== ChannelType.GuildAnnouncement) {
|
|
||||||
resolvedType = type ?? resolvedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await this.client.rest.post(Routes.threads(this.channel.id, startMessageId), {
|
|
||||||
body: {
|
|
||||||
name,
|
|
||||||
auto_archive_duration: autoArchiveDuration,
|
|
||||||
type: resolvedType,
|
|
||||||
invitable: resolvedType === ChannelType.PrivateThread ? invitable : undefined,
|
|
||||||
rate_limit_per_user: rateLimitPerUser,
|
|
||||||
},
|
|
||||||
reason,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.client.actions.ThreadCreate.handle(data).thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The options for fetching multiple threads, the properties are mutually exclusive
|
* The options for fetching multiple threads, the properties are mutually exclusive
|
||||||
* @typedef {Object} FetchThreadsOptions
|
* @typedef {Object} FetchThreadsOptions
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const { channelLink } = require('@discordjs/builders');
|
|||||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||||
const { ChannelType, Routes } = require('discord-api-types/v10');
|
const { ChannelType, Routes } = require('discord-api-types/v10');
|
||||||
const Base = require('./Base');
|
const Base = require('./Base');
|
||||||
|
const ChannelFlagsBitField = require('../util/ChannelFlagsBitField');
|
||||||
const { ThreadChannelTypes } = require('../util/Constants');
|
const { ThreadChannelTypes } = require('../util/Constants');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,6 +26,17 @@ class BaseChannel extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_patch(data) {
|
_patch(data) {
|
||||||
|
if ('flags' in data) {
|
||||||
|
/**
|
||||||
|
* The flags that are applied to the channel.
|
||||||
|
* <info>This is only `null` in a {@link PartialGroupDMChannel}. In all other cases, it is not `null`.</info>
|
||||||
|
* @type {?Readonly<ChannelFlagsBitField>}
|
||||||
|
*/
|
||||||
|
this.flags = new ChannelFlagsBitField(data.flags).freeze();
|
||||||
|
} else {
|
||||||
|
this.flags ??= new ChannelFlagsBitField().freeze();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The channel's id
|
* The channel's id
|
||||||
* @type {Snowflake}
|
* @type {Snowflake}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
const GuildChannel = require('./GuildChannel');
|
const GuildChannel = require('./GuildChannel');
|
||||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||||
|
const GuildTextThreadManager = require('../managers/GuildTextThreadManager');
|
||||||
const MessageManager = require('../managers/MessageManager');
|
const MessageManager = require('../managers/MessageManager');
|
||||||
const ThreadManager = require('../managers/ThreadManager');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a text-based guild channel on Discord.
|
* Represents a text-based guild channel on Discord.
|
||||||
@@ -22,9 +22,9 @@ class BaseGuildTextChannel extends GuildChannel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A manager of the threads belonging to this channel
|
* A manager of the threads belonging to this channel
|
||||||
* @type {ThreadManager}
|
* @type {GuildTextThreadManager}
|
||||||
*/
|
*/
|
||||||
this.threads = new ThreadManager(this);
|
this.threads = new GuildTextThreadManager(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the guild considers this channel NSFW
|
* If the guild considers this channel NSFW
|
||||||
|
|||||||
140
packages/discord.js/src/structures/ForumChannel.js
Normal file
140
packages/discord.js/src/structures/ForumChannel.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const GuildChannel = require('./GuildChannel');
|
||||||
|
const GuildForumThreadManager = require('../managers/GuildForumThreadManager');
|
||||||
|
const { transformAPIGuildForumTag, transformAPIGuildDefaultReaction } = require('../util/Channels');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GuildForumTagEmoji
|
||||||
|
* @property {?Snowflake} id The id of a guild's custom emoji
|
||||||
|
* @property {?string} name The unicode character of the emoji
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GuildForumTag
|
||||||
|
* @property {Snowflake} id The id of the tag
|
||||||
|
* @property {string} name The name of the tag
|
||||||
|
* @property {boolean} moderated Whether this tag can only be added to or removed from threads
|
||||||
|
* by a member with the `ManageThreads` permission
|
||||||
|
* @property {?GuildForumTagEmoji} emoji The emoji of this tag
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} GuildForumTagData
|
||||||
|
* @property {Snowflake} [id] The id of the tag
|
||||||
|
* @property {string} name The name of the tag
|
||||||
|
* @property {boolean} [moderated] Whether this tag can only be added to or removed from threads
|
||||||
|
* by a member with the `ManageThreads` permission
|
||||||
|
* @property {?GuildForumTagEmoji} [emoji] The emoji of this tag
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} DefaultReactionEmoji
|
||||||
|
* @property {?Snowflake} id The id of a guild's custom emoji
|
||||||
|
* @property {?string} name The unicode character of the emoji
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a channel that only contains threads
|
||||||
|
* @extends {GuildChannel}
|
||||||
|
*/
|
||||||
|
class ForumChannel extends GuildChannel {
|
||||||
|
constructor(guild, data, client) {
|
||||||
|
super(guild, data, client, false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager of the threads belonging to this channel
|
||||||
|
* @type {GuildForumThreadManager}
|
||||||
|
*/
|
||||||
|
this.threads = new GuildForumThreadManager(this);
|
||||||
|
|
||||||
|
this._patch(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_patch(data) {
|
||||||
|
super._patch(data);
|
||||||
|
if ('available_tags' in data) {
|
||||||
|
/**
|
||||||
|
* The set of tags that can be used in this channel.
|
||||||
|
* @type {GuildForumTag[]}
|
||||||
|
*/
|
||||||
|
this.availableTags = data.available_tags.map(tag => transformAPIGuildForumTag(tag));
|
||||||
|
} else {
|
||||||
|
this.availableTags ??= [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('default_reaction_emoji' in data) {
|
||||||
|
/**
|
||||||
|
* The emoji to show in the add reaction button on a thread in a guild forum channel
|
||||||
|
* @type {?DefaultReactionEmoji}
|
||||||
|
*/
|
||||||
|
this.defaultReactionEmoji = data.default_reaction_emoji
|
||||||
|
? transformAPIGuildDefaultReaction(data.default_reaction_emoji)
|
||||||
|
: null;
|
||||||
|
} else {
|
||||||
|
this.defaultReactionEmoji ??= null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('default_thread_rate_limit_per_user' in data) {
|
||||||
|
/**
|
||||||
|
* The initial rate limit per user (slowmode) to set on newly created threads in a channel.
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
this.defaultThreadRateLimitPerUser = data.default_thread_rate_limit_per_user;
|
||||||
|
} else {
|
||||||
|
this.defaultThreadRateLimitPerUser ??= null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('rate_limit_per_user' in data) {
|
||||||
|
/**
|
||||||
|
* The rate limit per user (slowmode) for this channel.
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
this.rateLimitPerUser = data.rate_limit_per_user;
|
||||||
|
} else {
|
||||||
|
this.rateLimitPerUser ??= null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the available tags for this forum channel
|
||||||
|
* @param {GuildForumTagData[]} availableTags The tags to set as available in this channel
|
||||||
|
* @param {string} [reason] Reason for changing the available tags
|
||||||
|
* @returns {Promise<ForumChannel>}
|
||||||
|
*/
|
||||||
|
setAvailableTags(availableTags, reason) {
|
||||||
|
return this.edit({ availableTags, reason });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default reaction emoji for this channel
|
||||||
|
* @param {?DefaultReactionEmoji} defaultReactionEmoji The emoji to set as the default reaction emoji
|
||||||
|
* @param {string} [reason] Reason for changing the default reaction emoji
|
||||||
|
* @returns {Promise<ForumChannel>}
|
||||||
|
*/
|
||||||
|
setDefaultReactionEmoji(defaultReactionEmoji, reason) {
|
||||||
|
return this.edit({ defaultReactionEmoji, reason });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default rate limit per user (slowmode) for new threads in this channel
|
||||||
|
* @param {number} defaultThreadRateLimitPerUser The rate limit to set on newly created threads in this channel
|
||||||
|
* @param {string} [reason] Reason for changing the default rate limit
|
||||||
|
* @returns {Promise<ForumChannel>}
|
||||||
|
*/
|
||||||
|
setDefaultThreadRateLimitPerUser(defaultThreadRateLimitPerUser, reason) {
|
||||||
|
return this.edit({ defaultThreadRateLimitPerUser, reason });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the rate limit per user (slowmode) for this channel
|
||||||
|
* @param {?number} rateLimitPerUser The rate limit to set on this channel
|
||||||
|
* @param {string} [reason] Reason for changing the rate limit
|
||||||
|
* @returns {Promise<ForumChannel>}
|
||||||
|
*/
|
||||||
|
setRateLimitPerUser(rateLimitPerUser, reason) {
|
||||||
|
return this.edit({ rateLimitPerUser, reason });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ForumChannel;
|
||||||
@@ -14,6 +14,7 @@ const PermissionsBitField = require('../util/PermissionsBitField');
|
|||||||
* - {@link CategoryChannel}
|
* - {@link CategoryChannel}
|
||||||
* - {@link NewsChannel}
|
* - {@link NewsChannel}
|
||||||
* - {@link StageChannel}
|
* - {@link StageChannel}
|
||||||
|
* - {@link ForumChannel}
|
||||||
* @extends {BaseChannel}
|
* @extends {BaseChannel}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -187,6 +187,17 @@ class Message extends Base {
|
|||||||
this.stickers = new Collection(this.stickers);
|
this.stickers = new Collection(this.stickers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('position' in data) {
|
||||||
|
/**
|
||||||
|
* A generally increasing integer (there may be gaps or duplicates) that represents
|
||||||
|
* the approximate position of the message in a thread.
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
this.position = data.position;
|
||||||
|
} else {
|
||||||
|
this.position ??= null;
|
||||||
|
}
|
||||||
|
|
||||||
// Discord sends null if the message has not been edited
|
// Discord sends null if the message has not been edited
|
||||||
if (data.edited_timestamp) {
|
if (data.edited_timestamp) {
|
||||||
/**
|
/**
|
||||||
@@ -800,7 +811,7 @@ class Message extends Base {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new public thread from this message
|
* Create a new public thread from this message
|
||||||
* @see ThreadManager#create
|
* @see GuildTextThreadManager#create
|
||||||
* @param {StartThreadOptions} [options] Options for starting a thread on this message
|
* @param {StartThreadOptions} [options] Options for starting a thread on this message
|
||||||
* @returns {Promise<ThreadChannel>}
|
* @returns {Promise<ThreadChannel>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -137,9 +137,11 @@ class MessagePayload {
|
|||||||
|
|
||||||
let username;
|
let username;
|
||||||
let avatarURL;
|
let avatarURL;
|
||||||
|
let threadName;
|
||||||
if (isWebhook) {
|
if (isWebhook) {
|
||||||
username = this.options.username ?? this.target.name;
|
username = this.options.username ?? this.target.name;
|
||||||
if (this.options.avatarURL) avatarURL = this.options.avatarURL;
|
if (this.options.avatarURL) avatarURL = this.options.avatarURL;
|
||||||
|
if (this.options.threadName) threadName = this.options.threadName;
|
||||||
}
|
}
|
||||||
|
|
||||||
let flags;
|
let flags;
|
||||||
@@ -207,6 +209,7 @@ class MessagePayload {
|
|||||||
message_reference,
|
message_reference,
|
||||||
attachments: this.options.attachments,
|
attachments: this.options.attachments,
|
||||||
sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker),
|
sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker),
|
||||||
|
thread_name: threadName,
|
||||||
};
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class PartialGroupDMChannel extends BaseChannel {
|
|||||||
constructor(client, data) {
|
constructor(client, data) {
|
||||||
super(client, data);
|
super(client, data);
|
||||||
|
|
||||||
|
// No flags are present when fetching partial group DM channels.
|
||||||
|
this.flags = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this Group DM Channel
|
* The name of this Group DM Channel
|
||||||
* @type {?string}
|
* @type {?string}
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ class ThreadChannel extends BaseChannel {
|
|||||||
if ('message_count' in data) {
|
if ('message_count' in data) {
|
||||||
/**
|
/**
|
||||||
* The approximate count of messages in this thread
|
* The approximate count of messages in this thread
|
||||||
* <info>This stops counting at 50. If you need an approximate value higher than that, use
|
* <info>Threads created before July 1, 2022 may have an inaccurate count.
|
||||||
* `ThreadChannel#messages.cache.size`</info>
|
* If you need an approximate value higher than that, use `ThreadChannel#messages.cache.size`</info>
|
||||||
* @type {?number}
|
* @type {?number}
|
||||||
*/
|
*/
|
||||||
this.messageCount = data.message_count;
|
this.messageCount = data.message_count;
|
||||||
@@ -180,8 +180,29 @@ class ThreadChannel extends BaseChannel {
|
|||||||
this.memberCount ??= null;
|
this.memberCount ??= null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('total_message_sent' in data) {
|
||||||
|
/**
|
||||||
|
* The number of messages ever sent in a thread, similar to {@link ThreadChannel#messageCount} except it
|
||||||
|
* will not decrement whenever a message is deleted
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
this.totalMessageSent = data.total_message_sent;
|
||||||
|
} else {
|
||||||
|
this.totalMessageSent ??= null;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.member && this.client.user) this.members._add({ user_id: this.client.user.id, ...data.member });
|
if (data.member && this.client.user) this.members._add({ user_id: this.client.user.id, ...data.member });
|
||||||
if (data.messages) for (const message of data.messages) this.messages._add(message);
|
if (data.messages) for (const message of data.messages) this.messages._add(message);
|
||||||
|
|
||||||
|
if ('applied_tags' in data) {
|
||||||
|
/**
|
||||||
|
* The tags applied to this thread
|
||||||
|
* @type {Snowflake[]}
|
||||||
|
*/
|
||||||
|
this.appliedTags = data.applied_tags;
|
||||||
|
} else {
|
||||||
|
this.appliedTags ??= [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -323,6 +344,7 @@ class ThreadChannel extends BaseChannel {
|
|||||||
rate_limit_per_user: data.rateLimitPerUser,
|
rate_limit_per_user: data.rateLimitPerUser,
|
||||||
locked: data.locked,
|
locked: data.locked,
|
||||||
invitable: this.type === ChannelType.PrivateThread ? data.invitable : undefined,
|
invitable: this.type === ChannelType.PrivateThread ? data.invitable : undefined,
|
||||||
|
applied_tags: data.appliedTags,
|
||||||
},
|
},
|
||||||
reason: data.reason,
|
reason: data.reason,
|
||||||
});
|
});
|
||||||
@@ -419,6 +441,16 @@ class ThreadChannel extends BaseChannel {
|
|||||||
return this.edit({ rateLimitPerUser, reason });
|
return this.edit({ rateLimitPerUser, reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the applied tags for this channel (only applicable to forum threads)
|
||||||
|
* @param {GuildForumTag[]} appliedTags The tags to set for this channel
|
||||||
|
* @param {string} [reason] Reason for changing the thread's applied tags
|
||||||
|
* @returns {Promise<GuildForumThreadChannel>}
|
||||||
|
*/
|
||||||
|
setAppliedTags(appliedTags, reason) {
|
||||||
|
return this.edit({ appliedTags, reason });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the client user is a member of the thread.
|
* Whether the client user is a member of the thread.
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ class Webhook {
|
|||||||
* @property {string} [avatarURL] Avatar URL override for the message
|
* @property {string} [avatarURL] Avatar URL override for the message
|
||||||
* @property {Snowflake} [threadId] The id of the thread in the channel to send to.
|
* @property {Snowflake} [threadId] The id of the thread in the channel to send to.
|
||||||
* <info>For interaction webhooks, this property is ignored</info>
|
* <info>For interaction webhooks, this property is ignored</info>
|
||||||
|
* @property {string} [threadName] Name of the thread to create (only available if webhook is in a forum channel)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -63,6 +63,16 @@
|
|||||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuild}
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuild}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @external APIGuildForumDefaultReactionEmoji
|
||||||
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuildForumDefaultReactionEmoji}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @external APIGuildForumTag
|
||||||
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuildForumTag}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @external APIInteraction
|
* @external APIInteraction
|
||||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10#APIInteraction}
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10#APIInteraction}
|
||||||
@@ -168,6 +178,11 @@
|
|||||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ButtonStyle}
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ButtonStyle}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @external ChannelFlags
|
||||||
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ChannelFlags}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @external ChannelType
|
* @external ChannelType
|
||||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ChannelType}
|
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ChannelType}
|
||||||
|
|||||||
32
packages/discord.js/src/util/ChannelFlagsBitField.js
Normal file
32
packages/discord.js/src/util/ChannelFlagsBitField.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { ChannelFlags } = require('discord-api-types/v10');
|
||||||
|
const BitField = require('./BitField');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure that makes it easy to interact with a {@link BaseChannel#flags} bitfield.
|
||||||
|
* @extends {BitField}
|
||||||
|
*/
|
||||||
|
class ChannelFlagsBitField extends BitField {
|
||||||
|
/**
|
||||||
|
* Numeric guild channel flags.
|
||||||
|
* @type {ChannelFlags}
|
||||||
|
* @memberof ChannelFlagsBitField
|
||||||
|
*/
|
||||||
|
static Flags = ChannelFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name ChannelFlagsBitField
|
||||||
|
* @kind constructor
|
||||||
|
* @memberof ChannelFlagsBitField
|
||||||
|
* @param {BitFieldResolvable} [bits=0] Bit(s) to read from
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitfield of the packed bits
|
||||||
|
* @type {number}
|
||||||
|
* @name ChannelFlagsBitField#bitfield
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = ChannelFlagsBitField;
|
||||||
@@ -12,6 +12,7 @@ const getThreadChannel = lazy(() => require('../structures/ThreadChannel'));
|
|||||||
const getVoiceChannel = lazy(() => require('../structures/VoiceChannel'));
|
const getVoiceChannel = lazy(() => require('../structures/VoiceChannel'));
|
||||||
const getDirectoryChannel = lazy(() => require('../structures/DirectoryChannel'));
|
const getDirectoryChannel = lazy(() => require('../structures/DirectoryChannel'));
|
||||||
const getPartialGroupDMChannel = lazy(() => require('../structures/PartialGroupDMChannel'));
|
const getPartialGroupDMChannel = lazy(() => require('../structures/PartialGroupDMChannel'));
|
||||||
|
const getForumChannel = lazy(() => require('../structures/ForumChannel'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a discord.js channel from data received from the API.
|
* Creates a discord.js channel from data received from the API.
|
||||||
@@ -65,6 +66,9 @@ function createChannel(client, data, guild, { allowUnknownGuild, fromInteraction
|
|||||||
case ChannelType.GuildDirectory:
|
case ChannelType.GuildDirectory:
|
||||||
channel = new (getDirectoryChannel())(guild, data, client);
|
channel = new (getDirectoryChannel())(guild, data, client);
|
||||||
break;
|
break;
|
||||||
|
case ChannelType.GuildForum:
|
||||||
|
channel = new (getForumChannel())(guild, data, client);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
|
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
|
||||||
}
|
}
|
||||||
@@ -72,6 +76,75 @@ function createChannel(client, data, guild, { allowUnknownGuild, fromInteraction
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an API guild forum tag to camel-cased guild forum tag.
|
||||||
|
* @param {APIGuildForumTag} tag The tag to transform
|
||||||
|
* @returns {GuildForumTag}
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
function transformAPIGuildForumTag(tag) {
|
||||||
|
return {
|
||||||
|
id: tag.id,
|
||||||
|
name: tag.name,
|
||||||
|
moderated: tag.moderated,
|
||||||
|
emoji:
|
||||||
|
tag.emoji_id ?? tag.emoji_name
|
||||||
|
? {
|
||||||
|
id: tag.emoji_id,
|
||||||
|
name: tag.emoji_name,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a camel-cased guild forum tag to an API guild forum tag.
|
||||||
|
* @param {GuildForumTag} tag The tag to transform
|
||||||
|
* @returns {APIGuildForumTag}
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
function transformGuildForumTag(tag) {
|
||||||
|
return {
|
||||||
|
id: tag.id,
|
||||||
|
name: tag.name,
|
||||||
|
moderated: tag.moderated,
|
||||||
|
emoji_id: tag.emoji?.id ?? null,
|
||||||
|
emoji_name: tag.emoji?.name ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an API guild forum default reaction object to a
|
||||||
|
* camel-cased guild forum default reaction object.
|
||||||
|
* @param {APIGuildForumDefaultReactionEmoji} defaultReaction The default reaction to transform
|
||||||
|
* @returns {DefaultReactionEmoji}
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
function transformAPIGuildDefaultReaction(defaultReaction) {
|
||||||
|
return {
|
||||||
|
id: defaultReaction.emoji_id,
|
||||||
|
name: defaultReaction.emoji_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a camel-cased guild forum default reaction object to an
|
||||||
|
* API guild forum default reaction object.
|
||||||
|
* @param {DefaultReactionEmoji} defaultReaction The default reaction to transform
|
||||||
|
* @returns {APIGuildForumDefaultReactionEmoji}
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
function transformGuildDefaultReaction(defaultReaction) {
|
||||||
|
return {
|
||||||
|
emoji_id: defaultReaction.id,
|
||||||
|
emoji_name: defaultReaction.name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createChannel,
|
createChannel,
|
||||||
|
transformAPIGuildForumTag,
|
||||||
|
transformGuildForumTag,
|
||||||
|
transformAPIGuildDefaultReaction,
|
||||||
|
transformGuildDefaultReaction,
|
||||||
};
|
};
|
||||||
|
|||||||
98
packages/discord.js/typings/index.d.ts
vendored
98
packages/discord.js/typings/index.d.ts
vendored
@@ -124,6 +124,7 @@ import {
|
|||||||
APIEmbedProvider,
|
APIEmbedProvider,
|
||||||
AuditLogOptionsType,
|
AuditLogOptionsType,
|
||||||
TextChannelType,
|
TextChannelType,
|
||||||
|
ChannelFlags,
|
||||||
} from 'discord-api-types/v10';
|
} from 'discord-api-types/v10';
|
||||||
import { ChildProcess } from 'node:child_process';
|
import { ChildProcess } from 'node:child_process';
|
||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter } from 'node:events';
|
||||||
@@ -507,7 +508,7 @@ export class BaseGuildTextChannel extends TextBasedChannelMixin(GuildChannel, tr
|
|||||||
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||||
public rateLimitPerUser: number | null;
|
public rateLimitPerUser: number | null;
|
||||||
public nsfw: boolean;
|
public nsfw: boolean;
|
||||||
public threads: ThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>;
|
public threads: GuildTextThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>;
|
||||||
public topic: string | null;
|
public topic: string | null;
|
||||||
public createInvite(options?: CreateInviteOptions): Promise<Invite>;
|
public createInvite(options?: CreateInviteOptions): Promise<Invite>;
|
||||||
public fetchInvites(cache?: boolean): Promise<Collection<string, Invite>>;
|
public fetchInvites(cache?: boolean): Promise<Collection<string, Invite>>;
|
||||||
@@ -726,11 +727,19 @@ export class CategoryChannel extends GuildChannel {
|
|||||||
|
|
||||||
export type CategoryChannelResolvable = Snowflake | CategoryChannel;
|
export type CategoryChannelResolvable = Snowflake | CategoryChannel;
|
||||||
|
|
||||||
|
export type ChannelFlagsString = keyof typeof ChannelFlags;
|
||||||
|
|
||||||
|
export class ChannelFlagsBitField extends BitField<ChannelFlagsString> {
|
||||||
|
public static Flags: typeof ChannelFlags;
|
||||||
|
public static resolve(bit?: BitFieldResolvable<ChannelFlagsString, ChannelFlags>): number;
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class BaseChannel extends Base {
|
export abstract class BaseChannel extends Base {
|
||||||
public constructor(client: Client<true>, data?: RawChannelData, immediatePatch?: boolean);
|
public constructor(client: Client<true>, data?: RawChannelData, immediatePatch?: boolean);
|
||||||
public get createdAt(): Date | null;
|
public get createdAt(): Date | null;
|
||||||
public get createdTimestamp(): number | null;
|
public get createdTimestamp(): number | null;
|
||||||
public id: Snowflake;
|
public id: Snowflake;
|
||||||
|
public flags: Readonly<ChannelFlagsBitField> | null;
|
||||||
public get partial(): false;
|
public get partial(): false;
|
||||||
public type: ChannelType;
|
public type: ChannelType;
|
||||||
public get url(): string;
|
public get url(): string;
|
||||||
@@ -1042,6 +1051,7 @@ export class DMChannel extends TextBasedChannelMixin(BaseChannel, false, [
|
|||||||
'setNSFW',
|
'setNSFW',
|
||||||
]) {
|
]) {
|
||||||
private constructor(client: Client<true>, data?: RawDMChannelData);
|
private constructor(client: Client<true>, data?: RawDMChannelData);
|
||||||
|
public flags: Readonly<ChannelFlagsBitField>;
|
||||||
public recipientId: Snowflake;
|
public recipientId: Snowflake;
|
||||||
public get recipient(): User | null;
|
public get recipient(): User | null;
|
||||||
public type: ChannelType.DM;
|
public type: ChannelType.DM;
|
||||||
@@ -1224,6 +1234,7 @@ export abstract class GuildChannel extends BaseChannel {
|
|||||||
public get createdAt(): Date;
|
public get createdAt(): Date;
|
||||||
public get createdTimestamp(): number;
|
public get createdTimestamp(): number;
|
||||||
public get deletable(): boolean;
|
public get deletable(): boolean;
|
||||||
|
public flags: Readonly<ChannelFlagsBitField>;
|
||||||
public guild: Guild;
|
public guild: Guild;
|
||||||
public guildId: Snowflake;
|
public guildId: Snowflake;
|
||||||
public get manageable(): boolean;
|
public get manageable(): boolean;
|
||||||
@@ -1698,6 +1709,7 @@ export class Message<InGuild extends boolean = boolean> extends Base {
|
|||||||
public pinned: boolean;
|
public pinned: boolean;
|
||||||
public reactions: ReactionManager;
|
public reactions: ReactionManager;
|
||||||
public stickers: Collection<Snowflake, Sticker>;
|
public stickers: Collection<Snowflake, Sticker>;
|
||||||
|
public position: number | null;
|
||||||
public system: boolean;
|
public system: boolean;
|
||||||
public get thread(): AnyThreadChannel | null;
|
public get thread(): AnyThreadChannel | null;
|
||||||
public tts: boolean;
|
public tts: boolean;
|
||||||
@@ -2018,7 +2030,7 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NewsChannel extends BaseGuildTextChannel {
|
export class NewsChannel extends BaseGuildTextChannel {
|
||||||
public threads: ThreadManager<AllowedThreadTypeForNewsChannel>;
|
public threads: GuildTextThreadManager<AllowedThreadTypeForNewsChannel>;
|
||||||
public type: ChannelType.GuildAnnouncement;
|
public type: ChannelType.GuildAnnouncement;
|
||||||
public addFollower(channel: TextChannelResolvable, reason?: string): Promise<NewsChannel>;
|
public addFollower(channel: TextChannelResolvable, reason?: string): Promise<NewsChannel>;
|
||||||
}
|
}
|
||||||
@@ -2032,6 +2044,7 @@ export class OAuth2Guild extends BaseGuild {
|
|||||||
export class PartialGroupDMChannel extends BaseChannel {
|
export class PartialGroupDMChannel extends BaseChannel {
|
||||||
private constructor(client: Client<true>, data: RawPartialGroupDMChannelData);
|
private constructor(client: Client<true>, data: RawPartialGroupDMChannelData);
|
||||||
public type: ChannelType.GroupDM;
|
public type: ChannelType.GroupDM;
|
||||||
|
public flags: null;
|
||||||
public name: string | null;
|
public name: string | null;
|
||||||
public icon: string | null;
|
public icon: string | null;
|
||||||
public recipients: PartialRecipient[];
|
public recipients: PartialRecipient[];
|
||||||
@@ -2039,6 +2052,39 @@ export class PartialGroupDMChannel extends BaseChannel {
|
|||||||
public toString(): ChannelMention;
|
public toString(): ChannelMention;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GuildForumTagEmoji {
|
||||||
|
id: Snowflake | null;
|
||||||
|
name: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GuildForumTag {
|
||||||
|
id: Snowflake;
|
||||||
|
name: string;
|
||||||
|
moderated: boolean;
|
||||||
|
emoji: GuildForumTagEmoji | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GuildForumTagData = Partial<GuildForumTag> & { name: string };
|
||||||
|
|
||||||
|
export interface DefaultReactionEmoji {
|
||||||
|
id: Snowflake | null;
|
||||||
|
name: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ForumChannel extends GuildChannel {
|
||||||
|
public type: ChannelType.GuildForum;
|
||||||
|
public threads: GuildForumThreadManager;
|
||||||
|
public availableTags: GuildForumTag[];
|
||||||
|
public defaultReactionEmoji: DefaultReactionEmoji | null;
|
||||||
|
public defaultThreadRateLimitPerUser: number | null;
|
||||||
|
public rateLimitPerUser: number | null;
|
||||||
|
|
||||||
|
public setAvailableTags(tags: GuildForumTagData[], reason?: string): Promise<this>;
|
||||||
|
public setDefaultReactionEmoji(emojiId: DefaultReactionEmoji | null, reason?: string): Promise<this>;
|
||||||
|
public setDefaultThreadRateLimitPerUser(rateLimit: number, reason?: string): Promise<this>;
|
||||||
|
public setRateLimitPerUser(rateLimitPerUser: number | null, reason?: string): Promise<this>;
|
||||||
|
}
|
||||||
|
|
||||||
export class PermissionOverwrites extends Base {
|
export class PermissionOverwrites extends Base {
|
||||||
private constructor(client: Client<true>, data: RawPermissionOverwriteData, channel: NonThreadGuildBasedChannel);
|
private constructor(client: Client<true>, data: RawPermissionOverwriteData, channel: NonThreadGuildBasedChannel);
|
||||||
public allow: Readonly<PermissionsBitField>;
|
public allow: Readonly<PermissionsBitField>;
|
||||||
@@ -2325,6 +2371,7 @@ export class StageChannel extends BaseGuildVoiceChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DirectoryChannel extends BaseChannel {
|
export class DirectoryChannel extends BaseChannel {
|
||||||
|
public flags: Readonly<ChannelFlagsBitField>;
|
||||||
public guild: InviteGuild;
|
public guild: InviteGuild;
|
||||||
public guildId: Snowflake;
|
public guildId: Snowflake;
|
||||||
public name: string;
|
public name: string;
|
||||||
@@ -2489,7 +2536,7 @@ export class TeamMember extends Base {
|
|||||||
|
|
||||||
export class TextChannel extends BaseGuildTextChannel {
|
export class TextChannel extends BaseGuildTextChannel {
|
||||||
public rateLimitPerUser: number;
|
public rateLimitPerUser: number;
|
||||||
public threads: ThreadManager<AllowedThreadTypeForTextChannel>;
|
public threads: GuildTextThreadManager<AllowedThreadTypeForTextChannel>;
|
||||||
public type: ChannelType.GuildText;
|
public type: ChannelType.GuildText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2519,6 +2566,7 @@ export class ThreadChannel extends TextBasedChannelMixin(BaseChannel, true, [
|
|||||||
public get createdTimestamp(): number | null;
|
public get createdTimestamp(): number | null;
|
||||||
public autoArchiveDuration: ThreadAutoArchiveDuration | null;
|
public autoArchiveDuration: ThreadAutoArchiveDuration | null;
|
||||||
public get editable(): boolean;
|
public get editable(): boolean;
|
||||||
|
public flags: Readonly<ChannelFlagsBitField>;
|
||||||
public guild: Guild;
|
public guild: Guild;
|
||||||
public guildId: Snowflake;
|
public guildId: Snowflake;
|
||||||
public get guildMembers(): Collection<Snowflake, GuildMember>;
|
public get guildMembers(): Collection<Snowflake, GuildMember>;
|
||||||
@@ -2531,6 +2579,8 @@ export class ThreadChannel extends TextBasedChannelMixin(BaseChannel, true, [
|
|||||||
public get sendable(): boolean;
|
public get sendable(): boolean;
|
||||||
public memberCount: number | null;
|
public memberCount: number | null;
|
||||||
public messageCount: number | null;
|
public messageCount: number | null;
|
||||||
|
public appliedTags: Snowflake[];
|
||||||
|
public totalMessageSent: number | null;
|
||||||
public members: ThreadMemberManager;
|
public members: ThreadMemberManager;
|
||||||
public name: string;
|
public name: string;
|
||||||
public ownerId: Snowflake | null;
|
public ownerId: Snowflake | null;
|
||||||
@@ -2558,6 +2608,7 @@ export class ThreadChannel extends TextBasedChannelMixin(BaseChannel, true, [
|
|||||||
public setInvitable(invitable?: boolean, reason?: string): Promise<AnyThreadChannel>;
|
public setInvitable(invitable?: boolean, reason?: string): Promise<AnyThreadChannel>;
|
||||||
public setLocked(locked?: boolean, reason?: string): Promise<AnyThreadChannel>;
|
public setLocked(locked?: boolean, reason?: string): Promise<AnyThreadChannel>;
|
||||||
public setName(name: string, reason?: string): Promise<AnyThreadChannel>;
|
public setName(name: string, reason?: string): Promise<AnyThreadChannel>;
|
||||||
|
public setAppliedTags(appliedTags: GuildForumTag[], reason?: string): Promise<AnyThreadChannel>;
|
||||||
public toString(): ChannelMention;
|
public toString(): ChannelMention;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3176,6 +3227,8 @@ export enum DiscordjsErrorCodes {
|
|||||||
NotImplemented = 'NotImplemented',
|
NotImplemented = 'NotImplemented',
|
||||||
|
|
||||||
SweepFilterReturn = 'SweepFilterReturn',
|
SweepFilterReturn = 'SweepFilterReturn',
|
||||||
|
|
||||||
|
GuildForumMessageRequired = 'GuildForumMessageRequired',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiscordjsErrorFields<Name extends string> {
|
export interface DiscordjsErrorFields<Name extends string> {
|
||||||
@@ -3620,16 +3673,23 @@ export class StageInstanceManager extends CachedManager<Snowflake, StageInstance
|
|||||||
public delete(channel: StageChannelResolvable): Promise<void>;
|
public delete(channel: StageChannelResolvable): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThreadManager<AllowedThreadType> extends CachedManager<Snowflake, ThreadChannel, ThreadChannelResolvable> {
|
export class ThreadManager extends CachedManager<Snowflake, ThreadChannel, ThreadChannelResolvable> {
|
||||||
private constructor(channel: TextChannel | NewsChannel, iterable?: Iterable<RawThreadChannelData>);
|
protected constructor(channel: TextChannel | NewsChannel, iterable?: Iterable<RawThreadChannelData>);
|
||||||
public channel: TextChannel | NewsChannel;
|
public channel: TextChannel | NewsChannel;
|
||||||
public create(options: ThreadCreateOptions<AllowedThreadType>): Promise<AnyThreadChannel>;
|
|
||||||
public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise<AnyThreadChannel | null>;
|
public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise<AnyThreadChannel | null>;
|
||||||
public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise<FetchedThreads>;
|
public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise<FetchedThreads>;
|
||||||
public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise<FetchedThreads>;
|
public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise<FetchedThreads>;
|
||||||
public fetchActive(cache?: boolean): Promise<FetchedThreads>;
|
public fetchActive(cache?: boolean): Promise<FetchedThreads>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GuildTextThreadManager<AllowedThreadType> extends ThreadManager {
|
||||||
|
public create(options: GuildTextThreadCreateOptions<AllowedThreadType>): Promise<ThreadChannel>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GuildForumThreadManager extends ThreadManager {
|
||||||
|
public create(options: GuildForumThreadCreateOptions): Promise<ThreadChannel>;
|
||||||
|
}
|
||||||
|
|
||||||
export class ThreadMemberManager extends CachedManager<Snowflake, ThreadMember, ThreadMemberResolvable> {
|
export class ThreadMemberManager extends CachedManager<Snowflake, ThreadMember, ThreadMemberResolvable> {
|
||||||
private constructor(thread: ThreadChannel, iterable?: Iterable<RawThreadMemberData>);
|
private constructor(thread: ThreadChannel, iterable?: Iterable<RawThreadMemberData>);
|
||||||
public thread: AnyThreadChannel;
|
public thread: AnyThreadChannel;
|
||||||
@@ -4158,6 +4218,8 @@ export interface CategoryCreateChannelOptions {
|
|||||||
position?: number;
|
position?: number;
|
||||||
rtcRegion?: string;
|
rtcRegion?: string;
|
||||||
videoQualityMode?: VideoQualityMode;
|
videoQualityMode?: VideoQualityMode;
|
||||||
|
availableTags?: GuildForumTagData[];
|
||||||
|
defaultReactionEmoji?: DefaultReactionEmoji;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4835,6 +4897,9 @@ export interface GuildChannelEditOptions {
|
|||||||
defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||||
rtcRegion?: string | null;
|
rtcRegion?: string | null;
|
||||||
videoQualityMode?: VideoQualityMode | null;
|
videoQualityMode?: VideoQualityMode | null;
|
||||||
|
availableTags?: GuildForumTagData[];
|
||||||
|
defaultReactionEmoji?: DefaultReactionEmoji | null;
|
||||||
|
defaultThreadRateLimitPerUser?: number;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5204,6 +5269,9 @@ export interface MessageCreateOptions extends BaseMessageOptions {
|
|||||||
flags?: BitFieldResolvable<Extract<MessageFlagsString, 'SuppressEmbeds'>, MessageFlags.SuppressEmbeds>;
|
flags?: BitFieldResolvable<Extract<MessageFlagsString, 'SuppressEmbeds'>, MessageFlags.SuppressEmbeds>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GuildForumThreadMessageCreateOptions = BaseMessageOptions &
|
||||||
|
Pick<MessageCreateOptions, 'flags' | 'stickers'>;
|
||||||
|
|
||||||
export interface MessageEditOptions extends Omit<BaseMessageOptions, 'content'> {
|
export interface MessageEditOptions extends Omit<BaseMessageOptions, 'content'> {
|
||||||
content?: string | null;
|
content?: string | null;
|
||||||
attachments?: JSONEncodable<AttachmentPayload>[];
|
attachments?: JSONEncodable<AttachmentPayload>[];
|
||||||
@@ -5538,9 +5606,13 @@ export type Channel =
|
|||||||
| StageChannel
|
| StageChannel
|
||||||
| TextChannel
|
| TextChannel
|
||||||
| AnyThreadChannel
|
| AnyThreadChannel
|
||||||
| VoiceChannel;
|
| VoiceChannel
|
||||||
|
| ForumChannel;
|
||||||
|
|
||||||
export type TextBasedChannel = Exclude<Extract<Channel, { type: TextChannelType }>, PartialGroupDMChannel>;
|
export type TextBasedChannel = Exclude<
|
||||||
|
Extract<Channel, { type: TextChannelType }>,
|
||||||
|
PartialGroupDMChannel | ForumChannel
|
||||||
|
>;
|
||||||
|
|
||||||
export type TextBasedChannelTypes = TextBasedChannel['type'];
|
export type TextBasedChannelTypes = TextBasedChannel['type'];
|
||||||
|
|
||||||
@@ -5552,7 +5624,7 @@ export type CategoryChildChannel = Exclude<Extract<Channel, { parent: CategoryCh
|
|||||||
|
|
||||||
export type NonThreadGuildBasedChannel = Exclude<GuildBasedChannel, AnyThreadChannel>;
|
export type NonThreadGuildBasedChannel = Exclude<GuildBasedChannel, AnyThreadChannel>;
|
||||||
|
|
||||||
export type GuildTextBasedChannel = Extract<GuildBasedChannel, TextBasedChannel>;
|
export type GuildTextBasedChannel = Exclude<Extract<GuildBasedChannel, TextBasedChannel>, ForumChannel>;
|
||||||
|
|
||||||
export type TextChannelResolvable = Snowflake | TextChannel;
|
export type TextChannelResolvable = Snowflake | TextChannel;
|
||||||
|
|
||||||
@@ -5562,12 +5634,17 @@ export type ThreadChannelResolvable = AnyThreadChannel | Snowflake;
|
|||||||
|
|
||||||
export type ThreadChannelType = ChannelType.AnnouncementThread | ChannelType.PublicThread | ChannelType.PrivateThread;
|
export type ThreadChannelType = ChannelType.AnnouncementThread | ChannelType.PublicThread | ChannelType.PrivateThread;
|
||||||
|
|
||||||
export interface ThreadCreateOptions<AllowedThreadType> extends StartThreadOptions {
|
export interface GuildTextThreadCreateOptions<AllowedThreadType> extends StartThreadOptions {
|
||||||
startMessage?: MessageResolvable;
|
startMessage?: MessageResolvable;
|
||||||
type?: AllowedThreadType;
|
type?: AllowedThreadType;
|
||||||
invitable?: AllowedThreadType extends ChannelType.PrivateThread ? boolean : never;
|
invitable?: AllowedThreadType extends ChannelType.PrivateThread ? boolean : never;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GuildForumThreadCreateOptions extends StartThreadOptions {
|
||||||
|
message: GuildForumThreadMessageCreateOptions | MessagePayload;
|
||||||
|
appliedTags?: Snowflake[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ThreadEditData {
|
export interface ThreadEditData {
|
||||||
name?: string;
|
name?: string;
|
||||||
archived?: boolean;
|
archived?: boolean;
|
||||||
@@ -5630,6 +5707,7 @@ export interface WebhookCreateMessageOptions extends Omit<MessageCreateOptions,
|
|||||||
username?: string;
|
username?: string;
|
||||||
avatarURL?: string;
|
avatarURL?: string;
|
||||||
threadId?: Snowflake;
|
threadId?: Snowflake;
|
||||||
|
threadName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WebSocketOptions {
|
export interface WebSocketOptions {
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ import {
|
|||||||
GuildAuditLogsActionType,
|
GuildAuditLogsActionType,
|
||||||
GuildAuditLogsTargetType,
|
GuildAuditLogsTargetType,
|
||||||
ModalSubmitInteraction,
|
ModalSubmitInteraction,
|
||||||
|
ForumChannel,
|
||||||
|
ChannelFlagsBitField,
|
||||||
} from '.';
|
} from '.';
|
||||||
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
||||||
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
|
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
|
||||||
@@ -1360,8 +1362,6 @@ declare const categoryChannelChildManager: CategoryChannelChildManager;
|
|||||||
|
|
||||||
declare const guildChannelManager: GuildChannelManager;
|
declare const guildChannelManager: GuildChannelManager;
|
||||||
{
|
{
|
||||||
type AnyChannel = TextChannel | VoiceChannel | CategoryChannel | NewsChannel | StageChannel;
|
|
||||||
|
|
||||||
expectType<Promise<TextChannel>>(guildChannelManager.create({ name: 'name' }));
|
expectType<Promise<TextChannel>>(guildChannelManager.create({ name: 'name' }));
|
||||||
expectType<Promise<TextChannel>>(guildChannelManager.create({ name: 'name' }));
|
expectType<Promise<TextChannel>>(guildChannelManager.create({ name: 'name' }));
|
||||||
expectType<Promise<VoiceChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildVoice }));
|
expectType<Promise<VoiceChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildVoice }));
|
||||||
@@ -1370,8 +1370,10 @@ declare const guildChannelManager: GuildChannelManager;
|
|||||||
expectType<Promise<NewsChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildAnnouncement }));
|
expectType<Promise<NewsChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildAnnouncement }));
|
||||||
expectType<Promise<StageChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildStageVoice }));
|
expectType<Promise<StageChannel>>(guildChannelManager.create({ name: 'name', type: ChannelType.GuildStageVoice }));
|
||||||
|
|
||||||
expectType<Promise<Collection<Snowflake, AnyChannel | null>>>(guildChannelManager.fetch());
|
expectType<Promise<Collection<Snowflake, NonThreadGuildBasedChannel | null>>>(guildChannelManager.fetch());
|
||||||
expectType<Promise<Collection<Snowflake, AnyChannel | null>>>(guildChannelManager.fetch(undefined, {}));
|
expectType<Promise<Collection<Snowflake, NonThreadGuildBasedChannel | null>>>(
|
||||||
|
guildChannelManager.fetch(undefined, {}),
|
||||||
|
);
|
||||||
expectType<Promise<GuildBasedChannel | null>>(guildChannelManager.fetch('0'));
|
expectType<Promise<GuildBasedChannel | null>>(guildChannelManager.fetch('0'));
|
||||||
|
|
||||||
const channel = guildChannelManager.cache.first()!;
|
const channel = guildChannelManager.cache.first()!;
|
||||||
@@ -1846,7 +1848,9 @@ expectType<
|
|||||||
>(TextBasedChannelTypes);
|
>(TextBasedChannelTypes);
|
||||||
expectType<StageChannel | VoiceChannel>(VoiceBasedChannel);
|
expectType<StageChannel | VoiceChannel>(VoiceBasedChannel);
|
||||||
expectType<GuildBasedChannel>(GuildBasedChannel);
|
expectType<GuildBasedChannel>(GuildBasedChannel);
|
||||||
expectType<CategoryChannel | NewsChannel | StageChannel | TextChannel | VoiceChannel>(NonThreadGuildBasedChannel);
|
expectType<CategoryChannel | NewsChannel | StageChannel | TextChannel | VoiceChannel | ForumChannel>(
|
||||||
|
NonThreadGuildBasedChannel,
|
||||||
|
);
|
||||||
expectType<GuildTextBasedChannel>(GuildTextBasedChannel);
|
expectType<GuildTextBasedChannel>(GuildTextBasedChannel);
|
||||||
|
|
||||||
const button = new ButtonBuilder({
|
const button = new ButtonBuilder({
|
||||||
@@ -1976,3 +1980,25 @@ expectType<Promise<APIMessage>>(webhookClient.fetchMessage(snowflake));
|
|||||||
expectType<Promise<Message>>(interactionWebhook.send('content'));
|
expectType<Promise<Message>>(interactionWebhook.send('content'));
|
||||||
expectType<Promise<Message>>(interactionWebhook.editMessage(snowflake, 'content'));
|
expectType<Promise<Message>>(interactionWebhook.editMessage(snowflake, 'content'));
|
||||||
expectType<Promise<Message>>(interactionWebhook.fetchMessage(snowflake));
|
expectType<Promise<Message>>(interactionWebhook.fetchMessage(snowflake));
|
||||||
|
|
||||||
|
declare const categoryChannel: CategoryChannel;
|
||||||
|
declare const forumChannel: ForumChannel;
|
||||||
|
|
||||||
|
await forumChannel.edit({
|
||||||
|
availableTags: [...forumChannel.availableTags, { name: 'tag' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await forumChannel.setAvailableTags([{ ...forumChannel.availableTags, name: 'tag' }]);
|
||||||
|
await forumChannel.setAvailableTags([{ name: 'tag' }]);
|
||||||
|
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(textChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(voiceChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(stageChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(forumChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(dmChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(categoryChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(newsChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(categoryChannel.flags);
|
||||||
|
expectType<Readonly<ChannelFlagsBitField>>(threadChannel.flags);
|
||||||
|
|
||||||
|
expectType<null>(partialGroupDMChannel.flags);
|
||||||
|
|||||||
Reference in New Issue
Block a user