mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat: api v9 and threads (#5570)
Co-authored-by: Noel <icrawltogo@gmail.com> Co-authored-by: Amish Shah <dev@shah.gg> Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com> Co-authored-by: SynthGhost <60333233+synthghost@users.noreply.github.com> Co-authored-by: SpaceEEC <24881032+SpaceEEC@users.noreply.github.com> Co-authored-by: Elliot <elliot@maisl.fr> Co-authored-by: Antonio Román <kyradiscord@gmail.com> Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com>
This commit is contained in:
@@ -34,6 +34,11 @@ class ActionsManager {
|
||||
this.register(require('./GuildEmojiDelete'));
|
||||
this.register(require('./GuildEmojiUpdate'));
|
||||
this.register(require('./GuildEmojisUpdate'));
|
||||
this.register(require('./ThreadCreate'));
|
||||
this.register(require('./ThreadDelete'));
|
||||
this.register(require('./ThreadListSync'));
|
||||
this.register(require('./ThreadMemberUpdate'));
|
||||
this.register(require('./ThreadMembersUpdate'));
|
||||
this.register(require('./GuildRolesPositionUpdate'));
|
||||
this.register(require('./GuildChannelsPositionUpdate'));
|
||||
this.register(require('./GuildIntegrationsUpdate'));
|
||||
|
||||
23
src/client/actions/ThreadCreate.js
Normal file
23
src/client/actions/ThreadCreate.js
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ThreadCreateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const existing = client.channels.cache.has(data.id);
|
||||
const thread = client.channels.add(data);
|
||||
if (!existing && thread) {
|
||||
/**
|
||||
* Emitted whenever a thread is created or when the client user is added to a thread.
|
||||
* @event Client#threadCreate
|
||||
* @param {ThreadChannel} thread The thread that was created
|
||||
*/
|
||||
client.emit(Events.THREAD_CREATE, thread);
|
||||
}
|
||||
return { thread };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadCreateAction;
|
||||
30
src/client/actions/ThreadDelete.js
Normal file
30
src/client/actions/ThreadDelete.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ThreadDeleteAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const thread = client.channels.cache.get(data.id);
|
||||
|
||||
if (thread) {
|
||||
client.channels.remove(thread.id);
|
||||
thread.deleted = true;
|
||||
for (const message of thread.messages.cache.values()) {
|
||||
message.deleted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a thread is deleted.
|
||||
* @event Client#threadDelete
|
||||
* @param {ThreadChannel} thread The thread that was deleted
|
||||
*/
|
||||
client.emit(Events.THREAD_DELETE, thread);
|
||||
}
|
||||
|
||||
return { thread };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadDeleteAction;
|
||||
59
src/client/actions/ThreadListSync.js
Normal file
59
src/client/actions/ThreadListSync.js
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const Collection = require('../../util/Collection');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ThreadListSyncAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
if (!guild) return {};
|
||||
|
||||
if (data.channels_ids) {
|
||||
for (const id of data.channel_ids) {
|
||||
const channel = client.channels.resolve(id);
|
||||
if (channel) this.removeStale(channel);
|
||||
}
|
||||
} else {
|
||||
for (const channel of guild.channels.cache.values()) {
|
||||
this.removeStale(channel);
|
||||
}
|
||||
}
|
||||
|
||||
const syncedThreads = data.threads.reduce((coll, rawThread) => {
|
||||
const thread = client.channels.add(rawThread);
|
||||
return coll.set(thread.id, thread);
|
||||
}, new Collection());
|
||||
|
||||
for (const rawMember of Object.values(data.members)) {
|
||||
// Discord sends the thread id as id in this object
|
||||
const thread = client.channels.cache.get(rawMember.id);
|
||||
if (thread) {
|
||||
thread.members._add(rawMember);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever the client user gains access to a text or news channel that contains threads
|
||||
* @event Client#threadListSync
|
||||
* @param {Collection<Snowflake, ThreadChannel>} threads The threads that were synced
|
||||
*/
|
||||
client.emit(Events.THREAD_LIST_SYNC, syncedThreads);
|
||||
|
||||
return {
|
||||
syncedThreads,
|
||||
};
|
||||
}
|
||||
|
||||
removeStale(channel) {
|
||||
channel.threads?.cache.forEach(thread => {
|
||||
if (!thread.archived) {
|
||||
this.client.channels.remove(thread.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadListSyncAction;
|
||||
30
src/client/actions/ThreadMemberUpdate.js
Normal file
30
src/client/actions/ThreadMemberUpdate.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ThreadMemberUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
// Discord sends the thread id as id in this object
|
||||
const thread = client.channels.cache.get(data.id);
|
||||
if (thread) {
|
||||
const member = thread.members.cache.get(data.user_id);
|
||||
if (!member) {
|
||||
const newMember = thread.members._add(data);
|
||||
return { newMember };
|
||||
}
|
||||
const old = member._update(data);
|
||||
/**
|
||||
* Emitted whenever the client user's thread member is updated.
|
||||
* @event Client#threadMemberUpdate
|
||||
* @param {ThreadMember} oldMember The member before the update
|
||||
* @param {ThreadMember} newMember The member after the update
|
||||
*/
|
||||
client.emit(Events.THREAD_MEMBER_UPDATE, old, member);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadMemberUpdateAction;
|
||||
34
src/client/actions/ThreadMembersUpdate.js
Normal file
34
src/client/actions/ThreadMembersUpdate.js
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
|
||||
class ThreadMembersUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const thread = client.channels.cache.get(data.id);
|
||||
if (thread) {
|
||||
const old = thread.members.cache.clone();
|
||||
thread.memberCount = data.member_count;
|
||||
|
||||
data.added_members?.forEach(rawMember => {
|
||||
thread.members._add(rawMember);
|
||||
});
|
||||
|
||||
data.removed_member_ids?.forEach(memberId => {
|
||||
thread.members.cache.delete(memberId);
|
||||
});
|
||||
|
||||
/**
|
||||
* Emitted whenever members are added or removed from a thread. Requires `GUILD_MEMBERS` privileged intent
|
||||
* @event Client#threadMembersUpdate
|
||||
* @param {Collection<Snowflake, ThreadMember>} oldMembers The members before the update
|
||||
* @param {Collection<Snowflake, ThreadMember>} newMembers The members after the update
|
||||
*/
|
||||
client.emit(Events.THREAD_MEMBERS_UPDATE, old, thread.members.cache);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadMembersUpdateAction;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const Action = require('./Action');
|
||||
const { Events } = require('../../util/Constants');
|
||||
const textBasedChannelTypes = ['dm', 'text', 'news'];
|
||||
const textBasedChannelTypes = ['dm', 'text', 'news', 'news_thread', 'public_thread', 'private_thread'];
|
||||
|
||||
class TypingStart extends Action {
|
||||
handle(data) {
|
||||
|
||||
5
src/client/websocket/handlers/THREAD_CREATE.js
Normal file
5
src/client/websocket/handlers/THREAD_CREATE.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.ThreadCreate.handle(packet.d);
|
||||
};
|
||||
5
src/client/websocket/handlers/THREAD_DELETE.js
Normal file
5
src/client/websocket/handlers/THREAD_DELETE.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.ThreadDelete.handle(packet.d);
|
||||
};
|
||||
5
src/client/websocket/handlers/THREAD_LIST_SYNC.js
Normal file
5
src/client/websocket/handlers/THREAD_LIST_SYNC.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.ThreadListSync.handle(packet.d);
|
||||
};
|
||||
5
src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js
Normal file
5
src/client/websocket/handlers/THREAD_MEMBERS_UPDATE.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.ThreadMembersUpdate.handle(packet.d);
|
||||
};
|
||||
5
src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js
Normal file
5
src/client/websocket/handlers/THREAD_MEMBER_UPDATE.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.ThreadMemberUpdate.handle(packet.d);
|
||||
};
|
||||
16
src/client/websocket/handlers/THREAD_UPDATE.js
Normal file
16
src/client/websocket/handlers/THREAD_UPDATE.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const { Events } = require('../../../util/Constants');
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
const { old, updated } = client.actions.ChannelUpdate.handle(packet.d);
|
||||
if (old && updated) {
|
||||
/**
|
||||
* Emitted whenever a thread is updated - e.g. name change, archive state change, locked state change.
|
||||
* @event Client#threadUpdate
|
||||
* @param {ThreadChannel} oldThread The thread before the update
|
||||
* @param {ThreadChannel} newThread The thread after the update
|
||||
*/
|
||||
client.emit(Events.THREAD_UPDATE, old, updated);
|
||||
}
|
||||
};
|
||||
@@ -92,6 +92,8 @@ const Messages = {
|
||||
INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
|
||||
INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
|
||||
|
||||
MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel',
|
||||
|
||||
WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
|
||||
WEBHOOK_TOKEN_UNAVAILABLE: 'This action requires a webhook token, but none is available.',
|
||||
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = {
|
||||
SnowflakeUtil: require('./util/SnowflakeUtil'),
|
||||
Structures: require('./util/Structures'),
|
||||
SystemChannelFlags: require('./util/SystemChannelFlags'),
|
||||
ThreadMemberFlags: require('./util/ThreadMemberFlags'),
|
||||
UserFlags: require('./util/UserFlags'),
|
||||
Util: require('./util/Util'),
|
||||
version: require('../package.json').version,
|
||||
@@ -47,6 +48,8 @@ module.exports = {
|
||||
MessageManager: require('./managers/MessageManager'),
|
||||
PresenceManager: require('./managers/PresenceManager'),
|
||||
RoleManager: require('./managers/RoleManager'),
|
||||
ThreadManager: require('./managers/ThreadManager'),
|
||||
ThreadMemberManager: require('./managers/ThreadMemberManager'),
|
||||
UserManager: require('./managers/UserManager'),
|
||||
|
||||
// Structures
|
||||
@@ -109,6 +112,8 @@ module.exports = {
|
||||
Team: require('./structures/Team'),
|
||||
TeamMember: require('./structures/TeamMember'),
|
||||
TextChannel: require('./structures/TextChannel'),
|
||||
ThreadChannel: require('./structures/ThreadChannel'),
|
||||
ThreadMember: require('./structures/ThreadMember'),
|
||||
User: require('./structures/User'),
|
||||
VoiceChannel: require('./structures/VoiceChannel'),
|
||||
VoiceRegion: require('./structures/VoiceRegion'),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const Channel = require('../structures/Channel');
|
||||
const { Events } = require('../util/Constants');
|
||||
const { Events, ThreadChannelTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* A manager of channels belonging to a client
|
||||
@@ -24,6 +24,9 @@ class ChannelManager extends BaseManager {
|
||||
if (existing) {
|
||||
if (existing._patch && cache) existing._patch(data);
|
||||
if (guild) guild.channels?.add(existing);
|
||||
if (ThreadChannelTypes.includes(existing.type) && typeof existing.parent?.threads !== 'undefined') {
|
||||
existing.parent.threads.add(existing);
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
|
||||
@@ -42,6 +45,7 @@ class ChannelManager extends BaseManager {
|
||||
remove(id) {
|
||||
const channel = this.cache.get(id);
|
||||
channel?.guild?.channels.cache.delete(id);
|
||||
channel?.parent?.threads?.cache.delete(id);
|
||||
this.cache.delete(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const BaseManager = require('./BaseManager');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const PermissionOverwrites = require('../structures/PermissionOverwrites');
|
||||
const Collection = require('../util/Collection');
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
const { ChannelTypes, ThreadChannelTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Manages API methods for GuildChannels and stores their cache.
|
||||
@@ -21,6 +21,19 @@ class GuildChannelManager extends BaseManager {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of channels in this managers cache excluding thread channels
|
||||
* that do not count towards a guild's maximum channels restriction.
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get channelCountWithoutThreads() {
|
||||
return this.cache.reduce((acc, channel) => {
|
||||
if (ThreadChannelTypes.includes(channel.type)) return acc;
|
||||
return ++acc;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this Manager
|
||||
* @type {Collection<Snowflake, GuildChannel>}
|
||||
|
||||
244
src/managers/ThreadManager.js
Normal file
244
src/managers/ThreadManager.js
Normal file
@@ -0,0 +1,244 @@
|
||||
'use strict';
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const ThreadChannel = require('../structures/ThreadChannel');
|
||||
const Collection = require('../util/Collection');
|
||||
const { ChannelTypes } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Manages API methods for ThreadChannels and stores their cache.
|
||||
* @extends {BaseManager}
|
||||
*/
|
||||
class ThreadManager extends BaseManager {
|
||||
constructor(channel, iterable) {
|
||||
super(channel.client, iterable, ThreadChannel);
|
||||
|
||||
/**
|
||||
* The channel this Manager belongs to
|
||||
* @type {NewsChannel|TextChannel}
|
||||
*/
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this Manager
|
||||
* @type {Collection<Snowflake, ThreadChannel>}
|
||||
* @name ThreadManager#cache
|
||||
*/
|
||||
|
||||
add(thread) {
|
||||
const existing = this.cache.get(thread.id);
|
||||
if (existing) return existing;
|
||||
this.cache.set(thread.id, thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a Thread Channel object. This can be:
|
||||
* * A ThreadChannel object
|
||||
* * A Snowflake
|
||||
* @typedef {ThreadChannel|Snowflake} ThreadChannelResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a ThreadChannelResolvable to a Thread Channel object.
|
||||
* @method resolve
|
||||
* @memberof ThreadManager
|
||||
* @instance
|
||||
* @param {ThreadChannelResolvable} thread The ThreadChannel resolvable to resolve
|
||||
* @returns {?ThreadChannel}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a ThreadChannelResolvable to a thread channel ID string.
|
||||
* @method resolveID
|
||||
* @memberof ThreadManager
|
||||
* @instance
|
||||
* @param {ThreadChannelResolvable} thread The ThreadChannel resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A number that is allowed to be the duration in minutes before a thread is automatically archived. This can be:
|
||||
* * `60` (1 hour)
|
||||
* * `1440` (1 day)
|
||||
* * `4320` (3 days)
|
||||
* * `10080` (7 days)
|
||||
* @typedef {number} ThreadAutoArchiveDuration
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for creating a thread <warn>Only one of `startMessage` or `type` can be defined.
|
||||
* If `startMessage` is defined, `type` is automatically defined and cannot be changed.</warn>
|
||||
* @typedef {Object} ThreadCreateOptions
|
||||
* @property {string} name The name of the new Thread
|
||||
* @property {ThreadAutoArchiveDuration} autoArchiveDuration How long before the thread is automatically archived
|
||||
* @property {MessageResolvable} [startMessage] The message to start a public or news thread from,
|
||||
* creates a private thread if not provided
|
||||
* @property {ThreadChannelType|number} [type] The type of thread to create
|
||||
* <warn>When creating threads in a `news` channel this is always `news_thread`</warn>
|
||||
* @param {string} [reason] Reason for creating the thread
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new thread in the channel.
|
||||
* @param {ThreadCreateOptions} [options] Options
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Create a new public thread
|
||||
* channel.threads
|
||||
* .create({
|
||||
* name: 'food-talk'
|
||||
* autoArchiveDuration: 60,
|
||||
* startMessage: channel.lastMessageID,
|
||||
* reason: 'Needed a separate thread for food',
|
||||
* })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
* // Create a new private thread
|
||||
* channel.threads
|
||||
* .create({ name: 'mod-talk', autoArchiveDuration: 60, reason: 'Needed a separate thread for moderation' })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async create({ name, autoArchiveDuration, startMessage, type, reason } = {}) {
|
||||
let path = this.client.api.channels(this.channel.id);
|
||||
if (type && typeof type !== 'string' && typeof type !== 'number') {
|
||||
throw new TypeError('INVALID_TYPE', 'type', 'ThreadChannelType or Number');
|
||||
}
|
||||
let resolvedType = this.channel.type === 'news' ? ChannelTypes.NEWS_THREAD : ChannelTypes.PUBLIC_THREAD;
|
||||
if (startMessage) {
|
||||
const startMessageID = this.channel.messages.resolveID(startMessage);
|
||||
if (!startMessageID) throw new TypeError('INVALID_TYPE', 'startMessage', 'MessageResolvable');
|
||||
path = path.messages(startMessageID);
|
||||
} else if (this.channel.type !== 'news') {
|
||||
resolvedType = typeof type === 'string' ? ChannelTypes[type.toUpperCase()] : type;
|
||||
}
|
||||
|
||||
const data = await path.threads.post({
|
||||
data: {
|
||||
name,
|
||||
auto_archive_duration: autoArchiveDuration,
|
||||
type: resolvedType,
|
||||
},
|
||||
reason,
|
||||
});
|
||||
|
||||
return this.client.actions.ThreadCreate.handle(data).thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* The options for fetching multiple threads, the properties are mutually exclusive
|
||||
* @typedef {Object} FetchThreadsOptions
|
||||
* @property {FetchArchivedThreadOptions} [archived] The options used to fetch archived threads
|
||||
* @property {boolean} [active] When true, fetches active threads. <warn>If `archived` is set, this is ignored!</warn>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains a thread from Discord, or the channel cache if it's already available.
|
||||
* @param {ThreadChannelResolvable|FetchThreadsOptions} [options] If a ThreadChannelResolvable, the thread to fetch.
|
||||
* If undefined, fetches all active threads.
|
||||
* If an Object, fetches the specified threads.
|
||||
* @param {BaseFetchOptions} [cacheOptions] Additional options for this fetch
|
||||
* only applies when fetching a single thread
|
||||
* @returns {Promise<?(ThreadChannel|FetchedThreads)>}
|
||||
* @example
|
||||
* // Fetch a thread by its id
|
||||
* channel.threads.fetch('831955138126104859')
|
||||
* .then(channel => console.log(channel.name))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetch(options, { cache = true, force = false } = {}) {
|
||||
if (!options) return this.fetchActive(cache);
|
||||
const channel = this.client.channels.resolveID(options);
|
||||
if (channel) return this.client.channels.fetch(channel, cache, force);
|
||||
if (options.archived) {
|
||||
return this.fetchArchived(options.archived, cache);
|
||||
}
|
||||
return this.fetchActive(cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a Date object. This can be:
|
||||
* * A Date object
|
||||
* * A number representing a timestamp
|
||||
* * An ISO8601 string
|
||||
* @typedef {Date|number|string} DateResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* The options used to fetch archived threads.
|
||||
* @typedef {Object} FetchArchivedThreadOptions
|
||||
* @property {string} [type='public'] The type of threads to fetch, either `public` or `private`
|
||||
* @property {boolean} [fetchAll=false] When type is `private` whether to fetch **all** archived threads,
|
||||
* requires `MANAGE_THREADS` if true
|
||||
* @property {DateResolvable|ThreadChannelResolvable} [before] Identifier for a Date or Snowflake
|
||||
* to get threads that were created before it,
|
||||
* must be a ThreadChannelResolvable when type is `private` and fetchAll is `false`
|
||||
* @property {number} [limit] Maximum number of threads to return
|
||||
*/
|
||||
|
||||
/**
|
||||
* The data returned from a thread fetch that returns multiple threads.
|
||||
* @typedef {FetchedThreads}
|
||||
* @property {Collection<Snowflake, ThreadChannel>} threads The threads fetched, with any members returned
|
||||
* @property {?boolean} hasMore Whether there are potentially additional threads that require a subsequent call
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains a set of archived threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel.
|
||||
* @param {FetchArchivedThreadOptions} [options] The options to use when fetch archived threads
|
||||
* @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already
|
||||
* @returns {Promise<FetchedThreads>}
|
||||
*/
|
||||
async fetchArchived({ type = 'public', fetchAll = false, before, limit } = {}, cache = true) {
|
||||
let path = this.client.api.channels(this.channel.id);
|
||||
if (type === 'private' && fetchAll) {
|
||||
path = path.users('@me');
|
||||
}
|
||||
let timestamp;
|
||||
let id;
|
||||
if (typeof before !== 'undefined') {
|
||||
if (before instanceof ThreadChannel || /^\d{16,19}$/.test(String(before))) {
|
||||
id = this.resolveID(before);
|
||||
timestamp = this.resolve(before)?.archivedAt?.toISOString();
|
||||
} else {
|
||||
try {
|
||||
timestamp = new Date(before).toISOString();
|
||||
} catch {
|
||||
throw new TypeError('INVALID_TYPE', 'before', 'DateResolvable or ThreadChannelResolvable');
|
||||
}
|
||||
}
|
||||
}
|
||||
const raw = await path.threads
|
||||
.archived(type)
|
||||
.get({ query: { before: type === 'private' && !fetchAll ? id : timestamp, limit } });
|
||||
return this._mapThreads(raw, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the accessible active threads from Discord, requires `READ_MESSAGE_HISTORY` in the parent channel.
|
||||
* @param {boolean} [cache=true] Whether to cache the new thread objects if they aren't already
|
||||
* @returns {Promise<FetchedThreads>}
|
||||
*/
|
||||
async fetchActive(cache = true) {
|
||||
const raw = await this.client.api.channels(this.channel.id).threads.active.get();
|
||||
return this._mapThreads(raw, cache);
|
||||
}
|
||||
|
||||
_mapThreads(rawThreads, cache) {
|
||||
const threads = rawThreads.threads.reduce((coll, raw) => {
|
||||
const thread = this.client.channels.add(raw, null, cache);
|
||||
return coll.set(thread.id, thread);
|
||||
}, new Collection());
|
||||
// Discord sends the thread id as id in this object
|
||||
for (const rawMember of rawThreads.members) threads.get(rawMember.id)?.members._add(rawMember);
|
||||
return {
|
||||
threads,
|
||||
hasMore: rawThreads.has_more,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadManager;
|
||||
112
src/managers/ThreadMemberManager.js
Normal file
112
src/managers/ThreadMemberManager.js
Normal file
@@ -0,0 +1,112 @@
|
||||
'use strict';
|
||||
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const ThreadMember = require('../structures/ThreadMember');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Manages API methods for GuildMembers and stores their cache.
|
||||
* @extends {BaseManager}
|
||||
*/
|
||||
class ThreadMemberManager extends BaseManager {
|
||||
constructor(thread, iterable) {
|
||||
super(thread.client, iterable, ThreadMember);
|
||||
/**
|
||||
* The thread this manager belongs to
|
||||
* @type {ThreadChannel}
|
||||
*/
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this Manager
|
||||
* @type {Collection<Snowflake, ThreadMember>}
|
||||
* @name ThreadMemberManager#cache
|
||||
*/
|
||||
|
||||
_add(data, cache = true) {
|
||||
const existing = this.cache.get(data.user_id);
|
||||
if (cache) existing?._patch(data);
|
||||
if (existing) return existing;
|
||||
|
||||
const member = new ThreadMember(this.thread, data);
|
||||
if (cache) this.cache.set(data.user_id, member);
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that resolves to give a ThreadMember object. This can be:
|
||||
* * A ThreadMember object
|
||||
* * A User resolvable
|
||||
* @typedef {ThreadMember|UserResolvable} ThreadMemberResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a ThreadMemberResolvable to a ThreadMember object.
|
||||
* @param {ThreadMemberResolvable} member The user that is part of the thread
|
||||
* @returns {?GuildMember}
|
||||
*/
|
||||
resolve(member) {
|
||||
const memberResolvable = super.resolve(member);
|
||||
if (memberResolvable) return memberResolvable;
|
||||
const userResolvable = this.client.users.resolveID(member);
|
||||
if (userResolvable) return super.resolve(userResolvable);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a ThreadMemberResolvable to a thread member ID string.
|
||||
* @param {ThreadMemberResolvable} member The user that is part of the guild
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveID(member) {
|
||||
const memberResolvable = super.resolveID(member);
|
||||
if (memberResolvable) return memberResolvable;
|
||||
const userResolvable = this.client.users.resolveID(member);
|
||||
return this.cache.has(userResolvable) ? userResolvable : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a member to the thread.
|
||||
* @param {UserResolvable|'@me'} member The member to add
|
||||
* @param {string} [reason] The reason for adding this member
|
||||
* @returns {Promise<Snowflake>}
|
||||
*/
|
||||
add(member, reason) {
|
||||
const id = member === '@me' ? member : this.client.users.resolveID(member);
|
||||
if (!id) return Promise.reject(new TypeError('INVALID_TYPE', 'member', 'UserResolvable'));
|
||||
return this.client.api
|
||||
.channels(this.id, 'thread-members', id)
|
||||
.put({ reason })
|
||||
.then(() => id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user from the thread.
|
||||
* @param {Snowflake|'@me'} id The id of the member to remove
|
||||
* @param {string} [reason] The reason for removing this member from the thread
|
||||
* @returns {Promise<Snowflake>}
|
||||
*/
|
||||
remove(id, reason) {
|
||||
return this.client.api
|
||||
.channels(this.thread.id, 'thread-members', id)
|
||||
.delete({ reason })
|
||||
.then(() => id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches member(s) for the thread from Discord, requires access to the `GUILD_MEMBERS` gateway intent.
|
||||
* @param {boolean} [cache=true] Whether or not to cache the fetched members
|
||||
* @returns {Promise<Collection<Snowflake, ThreadMember>>}
|
||||
*/
|
||||
async fetch(cache = true) {
|
||||
const raw = await this.client.api.channels(this.thread.id, 'thread-members').get();
|
||||
return raw.reduce((col, rawMember) => {
|
||||
const member = this.add(rawMember, cache);
|
||||
return col.set(member.id, member);
|
||||
}, new Collection());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadMemberManager;
|
||||
@@ -3,6 +3,7 @@
|
||||
const BaseManager = require('./BaseManager');
|
||||
const GuildMember = require('../structures/GuildMember');
|
||||
const Message = require('../structures/Message');
|
||||
const ThreadMember = require('../structures/ThreadMember');
|
||||
const User = require('../structures/User');
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,8 @@ class UserManager extends BaseManager {
|
||||
* * A Snowflake
|
||||
* * A Message object (resolves to the message author)
|
||||
* * A GuildMember object
|
||||
* @typedef {User|Snowflake|Message|GuildMember} UserResolvable
|
||||
* * A ThreadMember object
|
||||
* @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -35,7 +37,7 @@ class UserManager extends BaseManager {
|
||||
* @returns {?User}
|
||||
*/
|
||||
resolve(user) {
|
||||
if (user instanceof GuildMember) return user.user;
|
||||
if (user instanceof GuildMember || user instanceof ThreadMember) return user.user;
|
||||
if (user instanceof Message) return user.author;
|
||||
return super.resolve(user);
|
||||
}
|
||||
@@ -46,6 +48,7 @@ class UserManager extends BaseManager {
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveID(user) {
|
||||
if (user instanceof ThreadMember) return user.id;
|
||||
if (user instanceof GuildMember) return user.user.id;
|
||||
if (user instanceof Message) return user.author.id;
|
||||
return super.resolveID(user);
|
||||
|
||||
@@ -10,7 +10,7 @@ const SnowflakeUtil = require('../util/SnowflakeUtil');
|
||||
* @abstract
|
||||
*/
|
||||
class Channel extends Base {
|
||||
constructor(client, data) {
|
||||
constructor(client, data, immediatePatch = true) {
|
||||
super(client);
|
||||
|
||||
const type = ChannelTypes[data.type];
|
||||
@@ -22,6 +22,9 @@ class Channel extends Base {
|
||||
* * `category` - a guild category channel
|
||||
* * `news` - a guild news channel
|
||||
* * `store` - a guild store channel
|
||||
* * 'news_thread` - a guild news channels' public thread channel
|
||||
* * `public_thread` - a guild text channels' public thread channel
|
||||
* * `private_thread` - a guild text channels' private thread channel
|
||||
* * `stage` - a guild stage channel
|
||||
* * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
|
||||
* @type {string}
|
||||
@@ -34,7 +37,7 @@ class Channel extends Base {
|
||||
*/
|
||||
this.deleted = false;
|
||||
|
||||
if (data) this._patch(data);
|
||||
if (data && immediatePatch) this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
@@ -152,6 +155,14 @@ class Channel extends Base {
|
||||
channel = new StageChannel(guild, data);
|
||||
break;
|
||||
}
|
||||
case ChannelTypes.NEWS_THREAD:
|
||||
case ChannelTypes.PUBLIC_THREAD:
|
||||
case ChannelTypes.PRIVATE_THREAD: {
|
||||
const ThreadChannel = Structures.get('ThreadChannel');
|
||||
channel = new ThreadChannel(guild, data);
|
||||
channel.parent?.threads.cache.set(channel.id, channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel) guild.channels?.cache.set(channel.id, channel);
|
||||
}
|
||||
|
||||
@@ -338,6 +338,12 @@ class Guild extends AnonymousGuild {
|
||||
}
|
||||
}
|
||||
|
||||
if (data.threads) {
|
||||
for (const rawThread of data.threads) {
|
||||
this.client.channels.add(rawThread, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.roles) {
|
||||
this.roles.cache.clear();
|
||||
for (const role of data.roles) this.roles.add(role);
|
||||
|
||||
@@ -368,6 +368,8 @@ class GuildChannel extends Channel {
|
||||
* @property {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} [permissionOverwrites]
|
||||
* Permission overwrites for the channel
|
||||
* @property {number} [rateLimitPerUser] The ratelimit per user for the channel in seconds
|
||||
* @property {ThreadAutoArchiveDuration} [defaultAutoArchiveDuration]
|
||||
* The default auto archive duration for all new threads in this channel
|
||||
* @property {?string} [rtcRegion] The RTC region of the channel
|
||||
*/
|
||||
|
||||
@@ -428,6 +430,7 @@ class GuildChannel extends Channel {
|
||||
parent_id: data.parentID,
|
||||
lock_permissions: data.lockPermissions,
|
||||
rate_limit_per_user: data.rateLimitPerUser,
|
||||
default_auto_archive_duration: data.defaultAutoArchiveDuration,
|
||||
permission_overwrites,
|
||||
},
|
||||
reason,
|
||||
|
||||
@@ -101,6 +101,16 @@ class Message extends Base {
|
||||
this.pinned = null;
|
||||
}
|
||||
|
||||
if ('thread' in data) {
|
||||
/**
|
||||
* The thread started by this message
|
||||
* @type {?ThreadChannel}
|
||||
*/
|
||||
this.thread = this.client.channels.add(data.thread);
|
||||
} else if (!this.thread) {
|
||||
this.thread = null;
|
||||
}
|
||||
|
||||
if ('tts' in data) {
|
||||
/**
|
||||
* Whether or not the message was Text-To-Speech
|
||||
@@ -224,7 +234,7 @@ class Message extends Base {
|
||||
this.flags = new MessageFlags(data.flags).freeze();
|
||||
|
||||
/**
|
||||
* Reference data sent in a crossposted message or inline reply.
|
||||
* Reference data sent in a message that contains IDs identifying the referenced message
|
||||
* @typedef {Object} MessageReference
|
||||
* @property {string} channelID ID of the channel the message was referenced
|
||||
* @property {?string} guildID ID of the guild the message was referenced
|
||||
@@ -294,6 +304,7 @@ class Message extends Base {
|
||||
if ('content' in data) this.content = data.content;
|
||||
if ('pinned' in data) this.pinned = data.pinned;
|
||||
if ('tts' in data) this.tts = data.tts;
|
||||
if ('thread' in data) this.thread = this.client.channels.add(data.thread);
|
||||
if ('embeds' in data) this.embeds = data.embeds.map(e => new Embed(e, true));
|
||||
else this.embeds = this.embeds.slice();
|
||||
if ('components' in data) this.components = data.components.map(c => BaseMessageComponent.create(c, this.client));
|
||||
@@ -676,6 +687,21 @@ class Message extends Base {
|
||||
return this.channel.send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new public thread from this message
|
||||
* @see ThreadManager#create
|
||||
* @param {string} name The name of the new Thread
|
||||
* @param {ThreadAutoArchiveDuration} autoArchiveDuration How long before the thread is automatically archived
|
||||
* @param {string} [reason] Reason for creating the thread
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
*/
|
||||
startThread(name, autoArchiveDuration, reason) {
|
||||
if (!['text', 'news'].includes(this.channel.type)) {
|
||||
return Promise.reject(new Error('MESSAGE_THREAD_PARENT'));
|
||||
}
|
||||
return this.channel.threads.create({ name, autoArchiveDuration, startMessage: this, reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch this message.
|
||||
* @param {boolean} [force=false] Whether to skip the cache check and request the API
|
||||
|
||||
@@ -4,6 +4,7 @@ const GuildChannel = require('./GuildChannel');
|
||||
const Webhook = require('./Webhook');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const MessageManager = require('../managers/MessageManager');
|
||||
const ThreadManager = require('../managers/ThreadManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
|
||||
@@ -25,6 +26,12 @@ class TextChannel extends GuildChannel {
|
||||
*/
|
||||
this.messages = new MessageManager(this);
|
||||
|
||||
/**
|
||||
* A manager of the threads belonging to this channel
|
||||
* @type {ThreadManager}
|
||||
*/
|
||||
this.threads = new ThreadManager(this);
|
||||
|
||||
/**
|
||||
* If the guild considers this channel NSFW
|
||||
* @type {boolean}
|
||||
@@ -73,11 +80,29 @@ class TextChannel extends GuildChannel {
|
||||
this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
|
||||
}
|
||||
|
||||
if ('default_auto_archive_duration' in data) {
|
||||
/**
|
||||
* The default auto archive duration for newly created threads in this channel
|
||||
* @type {?ThreadAutoArchiveDuration}
|
||||
*/
|
||||
this.defaultAutoArchiveDuration = data.default_auto_archive_duration;
|
||||
}
|
||||
|
||||
if ('messages' in data) {
|
||||
for (const message of data.messages) this.messages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default auto archive duration for all newly created threads in this channel.
|
||||
* @param {ThreadAutoArchiveDuration} defaultAutoArchiveDuration The new default auto archive duration
|
||||
* @param {string} [reason] Reason for changing the channel's default auto archive duration
|
||||
* @returns {Promise<TextChannel>}
|
||||
*/
|
||||
setDefaultAutoArchiveDuration(defaultAutoArchiveDuration, reason) {
|
||||
return this.edit({ defaultAutoArchiveDuration }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rate limit per user for this channel.
|
||||
* <warn>It is not currently possible to set the rate limit per user on a `NewsChannel`.</warn>
|
||||
|
||||
379
src/structures/ThreadChannel.js
Normal file
379
src/structures/ThreadChannel.js
Normal file
@@ -0,0 +1,379 @@
|
||||
'use strict';
|
||||
|
||||
const Channel = require('./Channel');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const MessageManager = require('../managers/MessageManager');
|
||||
const ThreadMemberManager = require('../managers/ThreadMemberManager');
|
||||
const Permissions = require('../util/Permissions');
|
||||
|
||||
/**
|
||||
* Represents a thread channel on Discord.
|
||||
* @extends {Channel}
|
||||
* @implements {TextBasedChannel}
|
||||
*/
|
||||
class ThreadChannel extends Channel {
|
||||
/**
|
||||
* @param {Guild} guild The guild the thread channel is part of
|
||||
* @param {Object} data The data for the thread channel
|
||||
*/
|
||||
constructor(guild, data) {
|
||||
super(guild.client, data, false);
|
||||
|
||||
/**
|
||||
* The guild the thread is in
|
||||
* @type {Guild}
|
||||
*/
|
||||
this.guild = guild;
|
||||
|
||||
/**
|
||||
* A manager of the messages set to this thread
|
||||
* @type {MessageManager}
|
||||
*/
|
||||
this.messages = new MessageManager(this);
|
||||
|
||||
/**
|
||||
* A manager of the members that are part of this thread
|
||||
* @type {ThreadMemberManager}
|
||||
*/
|
||||
this.members = new ThreadMemberManager(this);
|
||||
|
||||
this._typing = new Map();
|
||||
if (data) this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
super._patch(data);
|
||||
|
||||
/**
|
||||
* The name of the thread
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = data.name;
|
||||
|
||||
/**
|
||||
* The ID of the parent channel to this thread
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.parentID = data.parent_id;
|
||||
|
||||
/**
|
||||
* Whether the thread is locked
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.locked = data.thread_metadata.locked ?? false;
|
||||
|
||||
/**
|
||||
* Whether the thread is active (false) or archived (true)
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.archived = data.thread_metadata.archived;
|
||||
|
||||
/**
|
||||
* The id of the member that created this thread
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.ownerID = data.owner_id;
|
||||
|
||||
/**
|
||||
* How long in minutes after recent activity before the thread is automatically archived
|
||||
* @type {number}
|
||||
*/
|
||||
this.autoArchiveDuration = data.thread_metadata.auto_archive_duration;
|
||||
|
||||
/**
|
||||
* The ID of the last message sent in this thread, if one was sent
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.lastMessageID = data.last_message_id;
|
||||
|
||||
/**
|
||||
* The timestamp when the last pinned message was pinned, if there was one
|
||||
* @type {?number}
|
||||
*/
|
||||
this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
|
||||
|
||||
/**
|
||||
* The ratelimit per user for this thread in seconds
|
||||
* @type {number}
|
||||
*/
|
||||
this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
|
||||
|
||||
/**
|
||||
* The timestamp the thread was last archived or unarchived at
|
||||
* @type {?number}
|
||||
*/
|
||||
this.archiveTimestamp = data.thread_metadata.archive_timestamp
|
||||
? new Date(data.thread_metadata.archive_timestamp).getTime()
|
||||
: null;
|
||||
|
||||
/**
|
||||
* The approximate count of messages in this thread
|
||||
* <info>This value will not count above 50 even when there are more than 50 messages
|
||||
* If you need an approximate value higher than this, use ThreadChannel#messages.cache.size</info>
|
||||
* @type {number}
|
||||
*/
|
||||
this.messageCount = data.message_count;
|
||||
|
||||
/**
|
||||
* The approximate count of users in this thread
|
||||
* <info>This value will not count above 50 even when there are more than 50 members</info>
|
||||
* @type {number}
|
||||
*/
|
||||
this.memberCount = data.member_count;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of the guild member objects for each of this thread's members
|
||||
* @type {Collection<Snowflake, GuildMember>}
|
||||
* @readonly
|
||||
*/
|
||||
get guildMembers() {
|
||||
return this.members.cache.mapValues(member => member.guildMember);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the thread was last archived or unarchived at
|
||||
* @type {?Date}
|
||||
* @readonly
|
||||
*/
|
||||
get archivedAt() {
|
||||
return this.archiveTimestamp ? new Date(this.archiveTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parent channel of this thread
|
||||
* @type {?(NewsChannel|TextChannel)}
|
||||
* @readonly
|
||||
*/
|
||||
get parent() {
|
||||
return this.guild.channels.resolve(this.parentID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the client user join the thread.
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
*/
|
||||
join() {
|
||||
return this.members.add('@me').then(() => this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the client user leave the thread.
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
*/
|
||||
leave() {
|
||||
return this.members.remove('@me').then(() => this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overall set of permissions for a member or role in this threads' parent, taking into account overwrites.
|
||||
* @param {GuildMemberResolvable|RoleResolvable} memberOrRole The member or role to obtain the overall permissions for
|
||||
* @returns {?Readonly<Permissions>}
|
||||
*/
|
||||
permissionsFor(memberOrRole) {
|
||||
return this.parent?.permissionsFor(memberOrRole) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the thread.
|
||||
* @param {Object} data The new data for the thread
|
||||
* @param {string} [data.name] The new name for the trhead
|
||||
* @param {boolean} [data.archived] Whether the thread is archived
|
||||
* @param {number} [data.autoArchiveDuration] How long in minutes before the thread is automatically archived,
|
||||
* one of `60`, `1440`, `4320`, or `10080`
|
||||
* @param {number} [data.rateLimitPerUser] The ratelimit per user for the thread in seconds
|
||||
* @param {boolean} [data.locked] Whether the thread is locked
|
||||
* @param {string} [reason] Reason for editing this thread
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Edit a thread
|
||||
* thread.edit({ name: 'new-thread' })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async edit(data, reason) {
|
||||
const newData = await this.client.api.channels(this.id).patch({
|
||||
data: {
|
||||
name: (data.name || this.name).trim(),
|
||||
archived: data.archived,
|
||||
auto_archive_duration: data.autoArchiveDuration,
|
||||
rate_limit_per_user: data.rateLimitPerUser,
|
||||
locked: data.locked,
|
||||
},
|
||||
reason,
|
||||
});
|
||||
|
||||
return this.client.actions.ChannelUpdate.handle(newData).updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the thread is archived.
|
||||
* @param {boolean} [archived=true] Whether the thread is archived
|
||||
* @param {string} [reason] Reason for archiving or unarchiving
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Set the thread to archived
|
||||
* thread.setArchived(true)
|
||||
* .then(newThread => console.log(`Thread is now ${newThread.archived ? 'archived' : 'active'}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setArchived(archived = true, reason) {
|
||||
return this.edit({ archived }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration before the channel is automatically archived.
|
||||
* @param {ThreadAutoArchiveDuration} autoArchiveDuration How long before the thread is automatically archived
|
||||
* @param {string} [reason] Reason for changing the archive time
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Set the thread auto archive time to 1 hour
|
||||
* thread.setAutoArchiveDuration(60)
|
||||
* .then(newThread => {
|
||||
* console.log(`Thread will now archive after ${newThread.autoArchiveDuration}`);
|
||||
* });
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setAutoArchiveDuration(autoArchiveDuration, reason) {
|
||||
return this.edit({ autoArchiveDuration }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the thread can be archived by anyone or just mods.
|
||||
* @param {boolean} [locked=true] Whether the thread is locked
|
||||
* @param {string} [reason] Reason for archiving or unarchiving
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Set the thread to locked
|
||||
* thread.setLocked(true)
|
||||
* .then(newThread => console.log(`Thread is now ${newThread.locked ? 'locked' : 'unlocked'}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setLocked(locked = true, reason) {
|
||||
return this.edit({ locked }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new name for the thread.
|
||||
* @param {string} name The new name for the thread
|
||||
* @param {string} [reason] Reason for changing the thread's name
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Set a new thread name
|
||||
* thread.setName('not_general')
|
||||
* .then(newThread => console.log(`Thread's new name is ${newThread.name}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setName(name, reason) {
|
||||
return this.edit({ name }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rate limit per user for this thread.
|
||||
* @param {number} rateLimitPerUser The new ratelimit in seconds
|
||||
* @param {string} [reason] Reason for changing the thread's ratelimits
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
*/
|
||||
setRateLimitPerUser(rateLimitPerUser, reason) {
|
||||
return this.edit({ rateLimitPerUser }, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is editable by the client user (name, archived, autoArchiveDuration)
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get editable() {
|
||||
return this.ownerID === this.client.user.id || this.manageable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is joinable by the client user
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get joinable() {
|
||||
return (
|
||||
!this.archived &&
|
||||
this.permissionsFor(this.client.user)?.has(
|
||||
this.type === 'private_thread' ? Permissions.FLAGS.MANAGE_THREADS : Permissions.FLAGS.VIEW_CHANNEL,
|
||||
false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is manageable by the client user, for deleting or editing rateLimitPerUser or locked.
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get manageable() {
|
||||
return this.permissionsFor(this.client.user)?.has(Permissions.FLAGS.MANAGE_THREADS, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client user can send messages in this thread
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get sendable() {
|
||||
return (
|
||||
!this.archived &&
|
||||
this.permissionsFor(this.client.user)?.any(
|
||||
[
|
||||
Permissions.FLAGS.SEND_MESSAGES,
|
||||
this.type === 'private_thread' ? Permissions.FLAGS.USE_PRIVATE_THREADS : Permissions.FLAGS.USE_PUBLIC_THREADS,
|
||||
],
|
||||
false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is unarchivable by the client user
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get unarchivable() {
|
||||
return this.archived && (this.locked ? this.manageable : this.sendable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this thread.
|
||||
* @param {string} [reason] Reason for deleting this thread
|
||||
* @returns {Promise<ThreadChannel>}
|
||||
* @example
|
||||
* // Delete the thread
|
||||
* thread.delete('cleaning out old threads')
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
delete(reason) {
|
||||
return this.client.api
|
||||
.channels(this.id)
|
||||
.delete({ reason })
|
||||
.then(() => this);
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||
/* eslint-disable no-empty-function */
|
||||
get lastMessage() {}
|
||||
get lastPinAt() {}
|
||||
send() {}
|
||||
startTyping() {}
|
||||
stopTyping() {}
|
||||
get typing() {}
|
||||
get typingCount() {}
|
||||
createMessageCollector() {}
|
||||
awaitMessages() {}
|
||||
createMessageComponentInteractionCollector() {}
|
||||
awaitMessageComponentInteractions() {}
|
||||
bulkDelete() {}
|
||||
}
|
||||
|
||||
TextBasedChannel.applyToClass(ThreadChannel, true);
|
||||
|
||||
module.exports = ThreadChannel;
|
||||
95
src/structures/ThreadMember.js
Normal file
95
src/structures/ThreadMember.js
Normal file
@@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
const ThreadMemberFlags = require('../util/ThreadMemberFlags');
|
||||
|
||||
/**
|
||||
* Represents a Member for a Thread.
|
||||
* @extends {Base}
|
||||
*/
|
||||
class ThreadMember extends Base {
|
||||
/**
|
||||
* @param {ThreadChannel} thread The thread that this member is associated with
|
||||
* @param {Object} data The data for the thread member
|
||||
*/
|
||||
constructor(thread, data) {
|
||||
super(thread.client);
|
||||
|
||||
/**
|
||||
* The thread that this member is a part of
|
||||
* @type {ThreadChannel}
|
||||
*/
|
||||
this.thread = thread;
|
||||
|
||||
/**
|
||||
* The timestamp the member last joined the thread at
|
||||
* @type {?number}
|
||||
*/
|
||||
this.joinedTimestamp = null;
|
||||
|
||||
/**
|
||||
* The id of the thread member
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.user_id;
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
this.joinedTimestamp = new Date(data.join_timestamp).getTime();
|
||||
|
||||
/**
|
||||
* The flags for this thread member
|
||||
* @type {ThreadMemberFlags}
|
||||
*/
|
||||
this.flags = new ThreadMemberFlags(data.flags).freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* The guild member that this thread member instance represents
|
||||
* @type {?GuildMember}
|
||||
* @readonly
|
||||
*/
|
||||
get guildMember() {
|
||||
return this.thread.guild.members.resolve(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* The last time this member joined the thread
|
||||
* @type {?Date}
|
||||
* @readonly
|
||||
*/
|
||||
get joinedAt() {
|
||||
return this.joinedTimestamp ? new Date(this.joinedTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user that this thread member instance represents
|
||||
* @type {?User}
|
||||
* @readonly
|
||||
*/
|
||||
get user() {
|
||||
return this.client.users.resolve(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client user can manage this thread member
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get manageable() {
|
||||
return !this.thread.archived && this.thread.editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this member from the thread.
|
||||
* @param {string} [reason] Reason for removing the member
|
||||
* @returns {ThreadMember}
|
||||
*/
|
||||
remove(reason) {
|
||||
return this.thread.members.remove(this.id, reason).then(() => this);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ThreadMember;
|
||||
@@ -92,6 +92,8 @@ class Webhook {
|
||||
* @typedef {BaseMessageOptions} WebhookMessageOptions
|
||||
* @property {string} [username=this.name] Username 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.
|
||||
* <info>For interaction webhooks, this property is ignored</info>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -115,6 +117,11 @@ class Webhook {
|
||||
* .then(message => console.log(`Sent message: ${message.content}`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Send a basic message in a thread
|
||||
* webhook.send('hello!', { threadID: '836856309672348295' })
|
||||
* .then(message => console.log(`Sent message: ${message.content}`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Send a remote file
|
||||
* webhook.send({
|
||||
* files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
|
||||
@@ -169,7 +176,7 @@ class Webhook {
|
||||
.post({
|
||||
data,
|
||||
files,
|
||||
query: { wait: true },
|
||||
query: { thread_id: apiMessage.options.threadID, wait: true },
|
||||
auth: false,
|
||||
})
|
||||
.then(d => {
|
||||
|
||||
@@ -91,13 +91,13 @@ exports.DefaultOptions = {
|
||||
$browser: 'discord.js',
|
||||
$device: 'discord.js',
|
||||
},
|
||||
version: 8,
|
||||
version: 9,
|
||||
},
|
||||
|
||||
/**
|
||||
* HTTP options
|
||||
* @typedef {Object} HTTPOptions
|
||||
* @property {number} [version=8] API version to use
|
||||
* @property {number} [version=9] API version to use
|
||||
* @property {string} [api='https://discord.com/api'] Base url of the API
|
||||
* @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN
|
||||
* @property {string} [invite='https://discord.gg'] Base url of invites
|
||||
@@ -105,7 +105,7 @@ exports.DefaultOptions = {
|
||||
* @property {Object} [headers] Additional headers to send for all API requests
|
||||
*/
|
||||
http: {
|
||||
version: 8,
|
||||
version: 9,
|
||||
api: 'https://discord.com/api',
|
||||
cdn: 'https://cdn.discordapp.com',
|
||||
invite: 'https://discord.gg',
|
||||
@@ -262,6 +262,12 @@ exports.Events = {
|
||||
MESSAGE_REACTION_REMOVE: 'messageReactionRemove',
|
||||
MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll',
|
||||
MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji',
|
||||
THREAD_CREATE: 'threadCreate',
|
||||
THREAD_DELETE: 'threadDelete',
|
||||
THREAD_UPDATE: 'threadUpdate',
|
||||
THREAD_LIST_SYNC: 'threadListSync',
|
||||
THREAD_MEMBER_UPDATE: 'threadMemberUpdate',
|
||||
THREAD_MEMBERS_UPDATE: 'threadMembersUpdate',
|
||||
USER_UPDATE: 'userUpdate',
|
||||
PRESENCE_UPDATE: 'presenceUpdate',
|
||||
VOICE_SERVER_UPDATE: 'voiceServerUpdate',
|
||||
@@ -341,6 +347,12 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE',
|
||||
* * MESSAGE_REACTION_REMOVE
|
||||
* * MESSAGE_REACTION_REMOVE_ALL
|
||||
* * MESSAGE_REACTION_REMOVE_EMOJI
|
||||
* * THREAD_CREATE
|
||||
* * THREAD_UPDATE
|
||||
* * THREAD_DELETE
|
||||
* * THREAD_LIST_SYNC
|
||||
* * THREAD_MEMBER_UPDATE
|
||||
* * THREAD_MEMBERS_UPDATE
|
||||
* * USER_UPDATE
|
||||
* * PRESENCE_UPDATE
|
||||
* * TYPING_START
|
||||
@@ -387,6 +399,12 @@ exports.WSEvents = keyMirror([
|
||||
'MESSAGE_REACTION_REMOVE',
|
||||
'MESSAGE_REACTION_REMOVE_ALL',
|
||||
'MESSAGE_REACTION_REMOVE_EMOJI',
|
||||
'THREAD_CREATE',
|
||||
'THREAD_UPDATE',
|
||||
'THREAD_DELETE',
|
||||
'THREAD_LIST_SYNC',
|
||||
'THREAD_MEMBER_UPDATE',
|
||||
'THREAD_MEMBERS_UPDATE',
|
||||
'USER_UPDATE',
|
||||
'PRESENCE_UPDATE',
|
||||
'TYPING_START',
|
||||
@@ -448,8 +466,11 @@ exports.InviteScopes = [
|
||||
* * GUILD_DISCOVERY_REQUALIFIED
|
||||
* * GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING
|
||||
* * GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING
|
||||
* * THREAD_CREATED
|
||||
* * REPLY
|
||||
* * APPLICATION_COMMAND
|
||||
* * THREAD_STARTER_MESSAGE
|
||||
* * GUILD_INVITE_REMINDER
|
||||
* @typedef {string} MessageType
|
||||
*/
|
||||
exports.MessageTypes = [
|
||||
@@ -471,9 +492,11 @@ exports.MessageTypes = [
|
||||
'GUILD_DISCOVERY_REQUALIFIED',
|
||||
'GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING',
|
||||
'GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING',
|
||||
null,
|
||||
'THREAD_CREATED',
|
||||
'REPLY',
|
||||
'APPLICATION_COMMAND',
|
||||
'THREAD_STARTER_MESSAGE',
|
||||
'GUILD_INVITE_REMINDER',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -509,11 +532,23 @@ exports.ChannelTypes = createEnum([
|
||||
'NEWS',
|
||||
// 6
|
||||
'STORE',
|
||||
...Array(6).fill(null),
|
||||
// 13
|
||||
...Array(3).fill(null),
|
||||
// 10
|
||||
'NEWS_THREAD',
|
||||
'PUBLIC_THREAD',
|
||||
'PRIVATE_THREAD',
|
||||
'STAGE',
|
||||
]);
|
||||
|
||||
/**
|
||||
* The types of channels that are threads. The available types are:
|
||||
* * news_thread
|
||||
* * public_thread
|
||||
* * private_thread
|
||||
* @typedef {string} ThreadChannelType
|
||||
*/
|
||||
exports.ThreadChannelTypes = ['news_thread', 'public_thread', 'private_thread'];
|
||||
|
||||
exports.ClientApplicationAssetTypes = {
|
||||
SMALL: 1,
|
||||
BIG: 2,
|
||||
@@ -629,6 +664,7 @@ exports.VerificationLevels = createEnum(['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_
|
||||
* * MAXIMUM_ANIMATED_EMOJIS
|
||||
* * MAXIMUM_SERVER_MEMBERS
|
||||
* * GUILD_ALREADY_HAS_TEMPLATE
|
||||
* * MAXIMUM_THREAD_PARTICIPANTS
|
||||
* * MAXIMUM_NON_GUILD_MEMBERS_BANS
|
||||
* * MAXIMUM_BAN_FETCHES
|
||||
* * UNAUTHORIZED
|
||||
@@ -672,11 +708,18 @@ exports.VerificationLevels = createEnum(['NONE', 'LOW', 'MEDIUM', 'HIGH', 'VERY_
|
||||
* * PAYMENT_SOURCE_REQUIRED
|
||||
* * CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL
|
||||
* * INVALID_STICKER_SENT
|
||||
* * INVALID_OPERATION_ON_ARCHIVED_THREAD
|
||||
* * INVALID_THREAD_NOTIFICATION_SETTINGS
|
||||
* * PARAMETER_EARLIER_THAN_CREATION
|
||||
* * TWO_FACTOR_REQUIRED
|
||||
* * NO_USERS_WITH_DISCORDTAG_EXIST
|
||||
* * REACTION_BLOCKED
|
||||
* * RESOURCE_OVERLOADED
|
||||
* * STAGE_ALREADY_OPEN
|
||||
* * MESSAGE_ALREADY_HAS_THREAD
|
||||
* * THREAD_LOCKED
|
||||
* * MAXIMUM_ACTIVE_THREADS
|
||||
* * MAXIMUM_ACTIVE_ANNOUCEMENT_THREAD
|
||||
* @typedef {string} APIError
|
||||
*/
|
||||
exports.APIErrors = {
|
||||
@@ -735,6 +778,7 @@ exports.APIErrors = {
|
||||
MAXIMUM_ANIMATED_EMOJIS: 30018,
|
||||
MAXIMUM_SERVER_MEMBERS: 30019,
|
||||
GUILD_ALREADY_HAS_TEMPLATE: 30031,
|
||||
MAXIMUM_THREAD_PARTICIPANTS: 30033,
|
||||
MAXIMUM_NON_GUILD_MEMBERS_BANS: 30035,
|
||||
MAXIMUM_BAN_FETCHES: 30037,
|
||||
UNAUTHORIZED: 40001,
|
||||
@@ -778,11 +822,18 @@ exports.APIErrors = {
|
||||
PAYMENT_SOURCE_REQUIRED: 50070,
|
||||
CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: 50074,
|
||||
INVALID_STICKER_SENT: 50081,
|
||||
INVALID_OPERATION_ON_ARCHIVED_THREAD: 50083,
|
||||
INVALID_THREAD_NOTIFICATION_SETTINGS: 50084,
|
||||
PARAMETER_EARLIER_THAN_CREATION: 50085,
|
||||
TWO_FACTOR_REQUIRED: 60003,
|
||||
NO_USERS_WITH_DISCORDTAG_EXIST: 80004,
|
||||
REACTION_BLOCKED: 90001,
|
||||
RESOURCE_OVERLOADED: 130000,
|
||||
STAGE_ALREADY_OPEN: 150006,
|
||||
MESSAGE_ALREADY_HAS_THREAD: 160004,
|
||||
THREAD_LOCKED: 160005,
|
||||
MAXIMUM_ACTIVE_THREADS: 160006,
|
||||
MAXIMUM_ACTIVE_ANNOUCEMENT_THREAD: 160007,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ class MessageFlags extends BitField {}
|
||||
* * `SUPPRESS_EMBEDS`
|
||||
* * `SOURCE_MESSAGE_DELETED`
|
||||
* * `URGENT`
|
||||
* * `HAS_THREAD`
|
||||
* * `EPHEMERAL`
|
||||
* * `LOADING`
|
||||
* @type {Object}
|
||||
@@ -39,6 +40,7 @@ MessageFlags.FLAGS = {
|
||||
SUPPRESS_EMBEDS: 1 << 2,
|
||||
SOURCE_MESSAGE_DELETED: 1 << 3,
|
||||
URGENT: 1 << 4,
|
||||
HAS_THREAD: 1 << 5,
|
||||
EPHEMERAL: 1 << 6,
|
||||
LOADING: 1 << 7,
|
||||
};
|
||||
|
||||
@@ -90,6 +90,9 @@ class Permissions extends BitField {
|
||||
* * `MANAGE_EMOJIS`
|
||||
* * `USE_APPLICATION_COMMANDS`
|
||||
* * `REQUEST_TO_SPEAK`
|
||||
* * `MANAGE_THREADS`
|
||||
* * `USE_PUBLIC_THREADS`
|
||||
* * `USE_PRIVATE_THREADS`
|
||||
* @type {Object<string, bigint>}
|
||||
* @see {@link https://discord.com/developers/docs/topics/permissions}
|
||||
*/
|
||||
@@ -127,6 +130,9 @@ Permissions.FLAGS = {
|
||||
MANAGE_EMOJIS: 1n << 30n,
|
||||
USE_APPLICATION_COMMANDS: 1n << 31n,
|
||||
REQUEST_TO_SPEAK: 1n << 32n,
|
||||
MANAGE_THREADS: 1n << 34n,
|
||||
USE_PUBLIC_THREADS: 1n << 35n,
|
||||
USE_PRIVATE_THREADS: 1n << 36n,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
* * **`NewsChannel`**
|
||||
* * **`StoreChannel`**
|
||||
* * **`StageChannel`**
|
||||
* * **`ThreadChannel`**
|
||||
* * **`GuildMember`**
|
||||
* * **`ThreadMember`**
|
||||
* * **`Guild`**
|
||||
* * **`Message`**
|
||||
* * **`MessageReaction`**
|
||||
@@ -103,7 +105,9 @@ const structures = {
|
||||
NewsChannel: require('../structures/NewsChannel'),
|
||||
StoreChannel: require('../structures/StoreChannel'),
|
||||
StageChannel: require('../structures/StageChannel'),
|
||||
ThreadChannel: require('../structures/ThreadChannel'),
|
||||
GuildMember: require('../structures/GuildMember'),
|
||||
ThreadMember: require('../structures/ThreadMember'),
|
||||
Guild: require('../structures/Guild'),
|
||||
Message: require('../structures/Message'),
|
||||
MessageReaction: require('../structures/MessageReaction'),
|
||||
|
||||
30
src/util/ThreadMemberFlags.js
Normal file
30
src/util/ThreadMemberFlags.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const BitField = require('./BitField');
|
||||
|
||||
/**
|
||||
* Data structure that makes it easy to interact with a {@link ThreadMember#flags} bitfield.
|
||||
* @extends {BitField}
|
||||
*/
|
||||
class ThreadMemberFlags extends BitField {}
|
||||
|
||||
/**
|
||||
* @name ThreadMemberFlags
|
||||
* @kind constructor
|
||||
* @memberof ThreadMemberFlags
|
||||
* @param {BitFieldResolvable} [bits=0] Bit(s) to read from
|
||||
*/
|
||||
|
||||
/**
|
||||
* Bitfield of the packed bits
|
||||
* @type {number}
|
||||
* @name ThreadMemberFlags#bitfield
|
||||
*/
|
||||
|
||||
/**
|
||||
* Numeric thread member flags. There are currently no bitflags relevant to bots for this.
|
||||
* @type {Object<string, number>}
|
||||
*/
|
||||
ThreadMemberFlags.FLAGS = {};
|
||||
|
||||
module.exports = ThreadMemberFlags;
|
||||
205
typings/index.d.ts
vendored
205
typings/index.d.ts
vendored
@@ -16,6 +16,9 @@ declare enum ChannelType {
|
||||
news = 5,
|
||||
store = 6,
|
||||
unknown = 7,
|
||||
news_thread = 10,
|
||||
public_thread = 11,
|
||||
private_thread = 12,
|
||||
stage = 13,
|
||||
}
|
||||
|
||||
@@ -27,6 +30,9 @@ declare enum ChannelTypes {
|
||||
CATEGORY = 4,
|
||||
NEWS = 5,
|
||||
STORE = 6,
|
||||
NEWS_THREAD = 10,
|
||||
PUBLIC_THREAD = 11,
|
||||
PRIVATE_THREAD = 12,
|
||||
STAGE = 13,
|
||||
}
|
||||
|
||||
@@ -371,7 +377,7 @@ declare module 'discord.js' {
|
||||
type CategoryChannelResolvable = Snowflake | CategoryChannel;
|
||||
|
||||
export class Channel extends Base {
|
||||
constructor(client: Client, data?: unknown);
|
||||
constructor(client: Client, data?: unknown, immediatePatch?: boolean);
|
||||
public readonly createdAt: Date;
|
||||
public readonly createdTimestamp: number;
|
||||
public deleted: boolean;
|
||||
@@ -379,7 +385,7 @@ declare module 'discord.js' {
|
||||
public type: keyof typeof ChannelType;
|
||||
public delete(reason?: string): Promise<Channel>;
|
||||
public fetch(force?: boolean): Promise<Channel>;
|
||||
public isText(): this is TextChannel | DMChannel | NewsChannel;
|
||||
public isText(): this is TextChannel | DMChannel | NewsChannel | ThreadChannel;
|
||||
public toString(): string;
|
||||
}
|
||||
|
||||
@@ -625,6 +631,12 @@ declare module 'discord.js' {
|
||||
MESSAGE_REACTION_REMOVE: 'messageReactionRemove';
|
||||
MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll';
|
||||
MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji';
|
||||
THREAD_CREATE: 'threadCreate';
|
||||
THREAD_DELETE: 'threadDelete';
|
||||
THREAD_UPDATE: 'threadUpdate';
|
||||
THREAD_LIST_SYNC: 'threadListSync';
|
||||
THREAD_MEMBER_UPDATE: 'threadMemberUpdate';
|
||||
THREAD_MEMBERS_UPDATE: 'threadMembersUpdate';
|
||||
USER_UPDATE: 'userUpdate';
|
||||
PRESENCE_UPDATE: 'presenceUpdate';
|
||||
VOICE_SERVER_UPDATE: 'voiceServerUpdate';
|
||||
@@ -715,6 +727,7 @@ declare module 'discord.js' {
|
||||
};
|
||||
APIErrors: APIErrors;
|
||||
ChannelTypes: typeof ChannelTypes;
|
||||
ThreadChannelTypes: ThreadChannelType[];
|
||||
ClientApplicationAssetTypes: {
|
||||
SMALL: 1;
|
||||
BIG: 2;
|
||||
@@ -1222,14 +1235,14 @@ declare module 'discord.js' {
|
||||
}
|
||||
|
||||
export class Message extends Base {
|
||||
constructor(client: Client, data: unknown, channel: TextChannel | DMChannel | NewsChannel);
|
||||
constructor(client: Client, data: unknown, channel: TextChannel | DMChannel | NewsChannel | ThreadChannel);
|
||||
private patch(data: unknown): Message;
|
||||
|
||||
public activity: MessageActivity | null;
|
||||
public applicationID: Snowflake | null;
|
||||
public attachments: Collection<Snowflake, MessageAttachment>;
|
||||
public author: User;
|
||||
public channel: TextChannel | DMChannel | NewsChannel;
|
||||
public channel: TextChannel | DMChannel | NewsChannel | ThreadChannel;
|
||||
public readonly cleanContent: string;
|
||||
public components: MessageActionRow[];
|
||||
public content: string;
|
||||
@@ -1255,6 +1268,7 @@ declare module 'discord.js' {
|
||||
public reactions: ReactionManager;
|
||||
public stickers: Collection<Snowflake, Sticker>;
|
||||
public system: boolean;
|
||||
public thread: ThreadChannel;
|
||||
public tts: boolean;
|
||||
public type: MessageType;
|
||||
public readonly url: string;
|
||||
@@ -1289,6 +1303,11 @@ declare module 'discord.js' {
|
||||
public removeAttachments(): Promise<Message>;
|
||||
public reply(options: string | APIMessage | (ReplyMessageOptions & { split?: false })): Promise<Message>;
|
||||
public reply(options: APIMessage | (ReplyMessageOptions & { split: true | SplitOptions })): Promise<Message[]>;
|
||||
public startThread(
|
||||
name: string,
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration,
|
||||
reason?: string,
|
||||
): Promise<ThreadChannel>;
|
||||
public suppressEmbeds(suppress?: boolean): Promise<Message>;
|
||||
public toJSON(): unknown;
|
||||
public toString(): string;
|
||||
@@ -1514,11 +1533,17 @@ declare module 'discord.js' {
|
||||
|
||||
export class NewsChannel extends TextBasedChannel(GuildChannel) {
|
||||
constructor(guild: Guild, data?: unknown);
|
||||
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||
public messages: MessageManager;
|
||||
public nsfw: boolean;
|
||||
public threads: ThreadManager;
|
||||
public topic: string | null;
|
||||
public type: 'news';
|
||||
public createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise<Webhook>;
|
||||
public setDefaultAutoArchiveDuration(
|
||||
defaultAutoArchiveDuration: ThreadAutoArchiveDuration,
|
||||
reason?: string,
|
||||
): Promise<NewsChannel>;
|
||||
public setNSFW(nsfw: boolean, reason?: string): Promise<NewsChannel>;
|
||||
public setType(type: Pick<typeof ChannelType, 'text' | 'news'>, reason?: string): Promise<GuildChannel>;
|
||||
public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
|
||||
@@ -1846,18 +1871,82 @@ declare module 'discord.js' {
|
||||
|
||||
export class TextChannel extends TextBasedChannel(GuildChannel) {
|
||||
constructor(guild: Guild, data?: unknown);
|
||||
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||
public messages: MessageManager;
|
||||
public nsfw: boolean;
|
||||
public type: 'text';
|
||||
public rateLimitPerUser: number;
|
||||
public threads: ThreadManager;
|
||||
public topic: string | null;
|
||||
public createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise<Webhook>;
|
||||
public setDefaultAutoArchiveDuration(
|
||||
defaultAutoArchiveDuration: ThreadAutoArchiveDuration,
|
||||
reason?: string,
|
||||
): Promise<TextChannel>;
|
||||
public setNSFW(nsfw: boolean, reason?: string): Promise<TextChannel>;
|
||||
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>;
|
||||
public setType(type: Pick<typeof ChannelType, 'text' | 'news'>, reason?: string): Promise<GuildChannel>;
|
||||
public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
|
||||
}
|
||||
|
||||
export class ThreadChannel extends TextBasedChannel(Channel) {
|
||||
constructor(guild: Guild, data?: object);
|
||||
public archived: boolean;
|
||||
public readonly archivedAt: Date | null;
|
||||
public archiveTimestamp: number | null;
|
||||
public autoArchiveDuration: ThreadAutoArchiveDuration;
|
||||
public readonly editable: boolean;
|
||||
public guild: Guild;
|
||||
public readonly guildMembers: Collection<Snowflake, GuildMember>;
|
||||
public readonly joinable: boolean;
|
||||
public locked: boolean;
|
||||
public readonly manageable: boolean;
|
||||
public readonly sendable: boolean;
|
||||
public memberCount: number | null;
|
||||
public messageCount: number | null;
|
||||
public messages: MessageManager;
|
||||
public members: ThreadMemberManager;
|
||||
public name: string;
|
||||
public ownerID: Snowflake;
|
||||
public readonly parent: TextChannel | NewsChannel | null;
|
||||
public parentID: Snowflake;
|
||||
public rateLimitPerUser: number;
|
||||
public type: ThreadChannelType;
|
||||
public readonly unarchivable: boolean;
|
||||
public delete(reason?: string): Promise<ThreadChannel>;
|
||||
public edit(data: ThreadEditData, reason?: string): Promise<ThreadChannel>;
|
||||
public join(): Promise<ThreadChannel>;
|
||||
public leave(): Promise<ThreadChannel>;
|
||||
public permissionsFor(memberOrRole: GuildMember | Role): Readonly<Permissions>;
|
||||
public permissionsFor(memberOrRole: GuildMemberResolvable | RoleResolvable): Readonly<Permissions> | null;
|
||||
public setArchived(archived: boolean, reason?: string): Promise<ThreadChannel>;
|
||||
public setAutoArchiveDuration(
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration,
|
||||
reason?: string,
|
||||
): Promise<ThreadChannel>;
|
||||
public setLocked(locked: boolean, reason?: string): Promise<ThreadChannel>;
|
||||
public setName(name: string, reason?: string): Promise<ThreadChannel>;
|
||||
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<ThreadChannel>;
|
||||
}
|
||||
|
||||
export class ThreadMember extends Base {
|
||||
constructor(thread: ThreadChannel, data?: object);
|
||||
public flags: ThreadMemberFlags;
|
||||
public readonly guildMember: GuildMember | null;
|
||||
public id: Snowflake;
|
||||
public readonly joinedAt: Date | null;
|
||||
public joinedTimestamp: number | null;
|
||||
public readonly manageable: boolean;
|
||||
public thread: ThreadChannel;
|
||||
public readonly user: User | null;
|
||||
public remove(reason?: string): Promise<ThreadMember>;
|
||||
}
|
||||
|
||||
export class ThreadMemberFlags extends BitField<ThreadMemberFlagsString> {
|
||||
public static FLAGS: Record<ThreadMemberFlagsString, number>;
|
||||
public static resolve(bit?: BitFieldResolvable<ThreadMemberFlagsString, number>): number;
|
||||
}
|
||||
|
||||
export class User extends PartialTextBasedChannel(Base) {
|
||||
constructor(client: Client, data: unknown);
|
||||
public avatar: string | null;
|
||||
@@ -2264,6 +2353,7 @@ declare module 'discord.js' {
|
||||
|
||||
export class GuildChannelManager extends BaseManager<Snowflake, GuildChannel, GuildChannelResolvable> {
|
||||
constructor(guild: Guild, iterable?: Iterable<any>);
|
||||
public readonly channelCountWithoutThreads: number;
|
||||
public guild: Guild;
|
||||
public create(name: string, options: GuildChannelCreateOptions & { type: 'voice' }): Promise<VoiceChannel>;
|
||||
public create(name: string, options: GuildChannelCreateOptions & { type: 'category' }): Promise<CategoryChannel>;
|
||||
@@ -2425,6 +2515,33 @@ declare module 'discord.js' {
|
||||
public delete(channel: StageChannel | Snowflake): Promise<void>;
|
||||
}
|
||||
|
||||
export class ThreadManager extends BaseManager<Snowflake, ThreadChannel, ThreadChannelResolvable> {
|
||||
constructor(channel: TextChannel | NewsChannel, iterable?: Iterable<any>);
|
||||
public channel: TextChannel | NewsChannel;
|
||||
public create(options: {
|
||||
name: string;
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration;
|
||||
startMessage?: MessageResolvable;
|
||||
reason?: string;
|
||||
}): Promise<ThreadChannel>;
|
||||
public fetch(options: ThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise<ThreadChannel | null>;
|
||||
public fetch(
|
||||
options?: { archived?: FetchArchivedThreadOptions; active?: boolean },
|
||||
cacheOptions?: { cache?: boolean },
|
||||
): Promise<FetchedThreads>;
|
||||
public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise<FetchedThreads>;
|
||||
public fetchActive(cache?: boolean): Promise<FetchedThreads>;
|
||||
}
|
||||
|
||||
export class ThreadMemberManager extends Omit<BaseManager<Snowflake, ThreadMember, ThreadMemberResolvable>, 'add'> {
|
||||
constructor(thread: ThreadChannel, iterable?: Iterable<any>);
|
||||
public thread: ThreadChannel;
|
||||
public _add(data: any, cache?: boolean): ThreadMember;
|
||||
public add(member: UserResolvable | '@me', reason?: string): Promise<Snowflake>;
|
||||
public fetch(cache?: boolean): Promise<Collection<Snowflake, ThreadMember>>;
|
||||
public remove(id: Snowflake | '@me', reason?: string): Promise<Snowflake>;
|
||||
}
|
||||
|
||||
export class UserManager extends BaseManager<Snowflake, User, UserResolvable> {
|
||||
constructor(client: Client, iterable?: Iterable<any>);
|
||||
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<User>;
|
||||
@@ -2595,6 +2712,7 @@ declare module 'discord.js' {
|
||||
MAXIMUM_ANIMATED_EMOJIS: 30018;
|
||||
MAXIMUM_SERVER_MEMBERS: 30019;
|
||||
GUILD_ALREADY_HAS_TEMPLATE: 30031;
|
||||
MAXIMUM_THREAD_PARICIPANTS: 30033;
|
||||
MAXIMUM_NON_GUILD_MEMBERS_BANS: 30035;
|
||||
MAXIMUM_BAN_FETCHES: 30037;
|
||||
UNAUTHORIZED: 40001;
|
||||
@@ -2638,11 +2756,18 @@ declare module 'discord.js' {
|
||||
PAYMENT_SOURCE_REQUIRED: 50070;
|
||||
CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: 50074;
|
||||
INVALID_STICKER_SENT: 50081;
|
||||
INVALID_THREAD_ARCHIVE_STATE: 50083;
|
||||
INVALID_THREAD_NOTIFICATION_SETTINGS: 50084;
|
||||
PARAMETER_EARLIER_THAN_CREATION: 50085;
|
||||
TWO_FACTOR_REQUIRED: 60003;
|
||||
NO_USERS_WITH_DISCORDTAG_EXIST: 80004;
|
||||
REACTION_BLOCKED: 90001;
|
||||
RESOURCE_OVERLOADED: 130000;
|
||||
STAGE_ALREADY_OPEN: 150006;
|
||||
MESSAGE_ALREADY_HAS_THREAD: 160004;
|
||||
THREAD_LOCKED: 160005;
|
||||
MAXIMUM_ACTIVE_THREADS: 160006;
|
||||
MAXIMUM_ACTIVE_ANNOUCEMENT_THREAD: 160007;
|
||||
}
|
||||
|
||||
interface ApplicationAsset {
|
||||
@@ -2765,6 +2890,7 @@ declare module 'discord.js' {
|
||||
rateLimitPerUser?: number;
|
||||
lockPermissions?: boolean;
|
||||
permissionOverwrites?: readonly OverwriteResolvable[] | Collection<Snowflake, OverwriteResolvable>;
|
||||
defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||
rtcRegion?: string | null;
|
||||
}
|
||||
|
||||
@@ -2838,7 +2964,16 @@ declare module 'discord.js' {
|
||||
roleCreate: [role: Role];
|
||||
roleDelete: [role: Role];
|
||||
roleUpdate: [oldRole: Role, newRole: Role];
|
||||
typingStart: [channel: TextChannel | NewsChannel | DMChannel | PartialDMChannel, user: User | PartialUser];
|
||||
threadCreate: [thread: ThreadChannel];
|
||||
threadDelete: [thread: ThreadChannel];
|
||||
threadListSync: [threads: Collection<Snowflake, ThreadChannel>];
|
||||
threadMemberUpdate: [oldMember: ThreadMember, newMember: ThreadMember];
|
||||
threadMembersUpdate: [
|
||||
oldMembers: Collection<Snowflake, ThreadMember>,
|
||||
mewMembers: Collection<Snowflake, ThreadMember>,
|
||||
];
|
||||
threadUpdate: [oldThread: ThreadChannel, newThread: ThreadChannel];
|
||||
typingStart: [channel: Channel | PartialDMChannel, user: User | PartialUser];
|
||||
userUpdate: [oldUser: User | PartialUser, newUser: User];
|
||||
voiceStateUpdate: [oldState: VoiceState, newState: VoiceState];
|
||||
webhookUpdate: [channel: TextChannel];
|
||||
@@ -2972,6 +3107,8 @@ declare module 'discord.js' {
|
||||
name: string;
|
||||
}
|
||||
|
||||
type DateResolvable = Date | number | string;
|
||||
|
||||
interface DeconstructedSnowflake {
|
||||
timestamp: number;
|
||||
readonly date: Date;
|
||||
@@ -3033,7 +3170,9 @@ declare module 'discord.js' {
|
||||
CategoryChannel: typeof CategoryChannel;
|
||||
NewsChannel: typeof NewsChannel;
|
||||
StoreChannel: typeof StoreChannel;
|
||||
ThreadChannel: typeof ThreadChannel;
|
||||
GuildMember: typeof GuildMember;
|
||||
ThreadMember: typeof ThreadMember;
|
||||
Guild: typeof Guild;
|
||||
Message: typeof Message;
|
||||
MessageReaction: typeof MessageReaction;
|
||||
@@ -3067,6 +3206,18 @@ declare module 'discord.js' {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
interface FetchArchivedThreadOptions {
|
||||
type?: 'public' | 'private';
|
||||
fetchAll?: boolean;
|
||||
before?: ThreadChannelResolvable | DateResolvable;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
interface FetchedThreads {
|
||||
threads: Collection<Snowflake, ThreadChannel>;
|
||||
hasMore?: boolean;
|
||||
}
|
||||
|
||||
interface FetchMemberOptions extends BaseFetchOptions {
|
||||
user: UserResolvable;
|
||||
}
|
||||
@@ -3182,7 +3333,16 @@ declare module 'discord.js' {
|
||||
topic?: string;
|
||||
type?: Exclude<
|
||||
keyof typeof ChannelType | ChannelType,
|
||||
'dm' | 'group' | 'unknown' | ChannelType.dm | ChannelType.group | ChannelType.unknown
|
||||
| 'dm'
|
||||
| 'group'
|
||||
| 'unknown'
|
||||
| 'public_thread'
|
||||
| 'private_thread'
|
||||
| ChannelType.dm
|
||||
| ChannelType.group
|
||||
| ChannelType.unknown
|
||||
| ChannelType.public_thread
|
||||
| ChannelType.private_thread
|
||||
>;
|
||||
nsfw?: boolean;
|
||||
parent?: ChannelResolvable;
|
||||
@@ -3535,6 +3695,7 @@ declare module 'discord.js' {
|
||||
| 'SUPPRESS_EMBEDS'
|
||||
| 'SOURCE_MESSAGE_DELETED'
|
||||
| 'URGENT'
|
||||
| 'HAS_THREAD'
|
||||
| 'EPHEMERAL'
|
||||
| 'LOADING';
|
||||
|
||||
@@ -3594,6 +3755,7 @@ declare module 'discord.js' {
|
||||
| InteractionWebhook
|
||||
| TextChannel
|
||||
| NewsChannel
|
||||
| ThreadChannel
|
||||
| DMChannel
|
||||
| User
|
||||
| GuildMember
|
||||
@@ -3620,8 +3782,11 @@ declare module 'discord.js' {
|
||||
| 'GUILD_DISCOVERY_REQUALIFIED'
|
||||
| 'GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING'
|
||||
| 'GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING'
|
||||
| 'THREAD_CREATED'
|
||||
| 'REPLY'
|
||||
| 'APPLICATION_COMMAND';
|
||||
| 'APPLICATION_COMMAND'
|
||||
| 'THREAD_STARTER_MESSAGE'
|
||||
| 'GUILD_INVITE_REMINDER';
|
||||
|
||||
type MFALevel = keyof typeof MFALevels;
|
||||
|
||||
@@ -3691,7 +3856,10 @@ declare module 'discord.js' {
|
||||
| 'MANAGE_WEBHOOKS'
|
||||
| 'MANAGE_EMOJIS'
|
||||
| 'USE_APPLICATION_COMMANDS'
|
||||
| 'REQUEST_TO_SPEAK';
|
||||
| 'REQUEST_TO_SPEAK'
|
||||
| 'MANAGE_THREADS'
|
||||
| 'USE_PUBLIC_THREADS'
|
||||
| 'USE_PRIVATE_THREADS';
|
||||
|
||||
interface RecursiveArray<T> extends ReadonlyArray<T | RecursiveArray<T>> {}
|
||||
|
||||
@@ -3969,6 +4137,24 @@ declare module 'discord.js' {
|
||||
privacyLevel?: PrivacyLevel | number;
|
||||
}
|
||||
|
||||
type ThreadAutoArchiveDuration = 60 | 1440 | 4320 | 10080;
|
||||
|
||||
type ThreadChannelResolvable = ThreadChannel | Snowflake;
|
||||
|
||||
type ThreadChannelType = 'news_thread' | 'public_thread' | 'private_thread';
|
||||
|
||||
interface ThreadEditData {
|
||||
name?: string;
|
||||
archived?: boolean;
|
||||
autoArchiveDuration?: ThreadAutoArchiveDuration;
|
||||
rateLimitPeruser?: number;
|
||||
locked?: boolean;
|
||||
}
|
||||
|
||||
type ThreadMemberFlagsString = '';
|
||||
|
||||
type ThreadMemberResolvable = ThreadMember | UserResolvable;
|
||||
|
||||
type UserFlagsString =
|
||||
| 'DISCORD_EMPLOYEE'
|
||||
| 'PARTNERED_SERVER_OWNER'
|
||||
@@ -3984,7 +4170,7 @@ declare module 'discord.js' {
|
||||
| 'EARLY_VERIFIED_BOT_DEVELOPER'
|
||||
| 'DISCORD_CERTIFIED_MODERATOR';
|
||||
|
||||
type UserResolvable = User | Snowflake | Message | GuildMember;
|
||||
type UserResolvable = User | Snowflake | Message | GuildMember | ThreadMember;
|
||||
|
||||
interface Vanity {
|
||||
code: string | null;
|
||||
@@ -4012,6 +4198,7 @@ declare module 'discord.js' {
|
||||
interface WebhookMessageOptions extends Omit<MessageOptions, 'reply'> {
|
||||
username?: string;
|
||||
avatarURL?: string;
|
||||
threadID?: Snowflake;
|
||||
}
|
||||
|
||||
type WebhookType = keyof typeof WebhookTypes;
|
||||
|
||||
Reference in New Issue
Block a user