diff --git a/src/managers/ChannelManager.js b/src/managers/ChannelManager.js
index 4de961bc248b..357c5af22539 100644
--- a/src/managers/ChannelManager.js
+++ b/src/managers/ChannelManager.js
@@ -19,10 +19,10 @@ class ChannelManager extends CachedManager {
* @name ChannelManager#cache
*/
- _add(data, guild, cache = true, allowUnknownGuild = false) {
+ _add(data, guild, { cache = true, allowUnknownGuild = false, fromInteraction = false } = {}) {
const existing = this.cache.get(data.id);
if (existing) {
- if (cache) existing._patch(data);
+ if (cache) existing._patch(data, fromInteraction);
guild?.channels?._add(existing);
if (ThreadChannelTypes.includes(existing.type)) {
existing.parent?.threads?._add(existing);
@@ -30,7 +30,7 @@ class ChannelManager extends CachedManager {
return existing;
}
- const channel = Channel.create(this.client, data, guild, allowUnknownGuild);
+ const channel = Channel.create(this.client, data, guild, { allowUnknownGuild, fromInteraction });
if (!channel) {
this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`);
@@ -99,7 +99,7 @@ class ChannelManager extends CachedManager {
}
const data = await this.client.api.channels(id).get();
- return this._add(data, null, cache, allowUnknownGuild);
+ return this._add(data, null, { cache, allowUnknownGuild });
}
}
diff --git a/src/managers/GuildChannelManager.js b/src/managers/GuildChannelManager.js
index 46fdd6cdaaee..075315c48557 100644
--- a/src/managers/GuildChannelManager.js
+++ b/src/managers/GuildChannelManager.js
@@ -169,12 +169,12 @@ class GuildChannelManager extends CachedManager {
const data = await this.client.api.channels(id).get();
// Since this is the guild manager, throw if on a different guild
if (this.guild.id !== data.guild_id) throw new Error('GUILD_CHANNEL_UNOWNED');
- return this.client.channels._add(data, this.guild, cache);
+ return this.client.channels._add(data, this.guild, { cache });
}
const data = await this.client.api.guilds(this.guild.id).channels.get();
const channels = new Collection();
- for (const channel of data) channels.set(channel.id, this.client.channels._add(channel, this.guild, cache));
+ for (const channel of data) channels.set(channel.id, this.client.channels._add(channel, this.guild, { cache }));
return channels;
}
}
diff --git a/src/managers/ThreadManager.js b/src/managers/ThreadManager.js
index 4853791cfa73..43141f1bd420 100644
--- a/src/managers/ThreadManager.js
+++ b/src/managers/ThreadManager.js
@@ -235,7 +235,7 @@ class ThreadManager extends CachedManager {
_mapThreads(rawThreads, cache) {
const threads = rawThreads.threads.reduce((coll, raw) => {
- const thread = this.client.channels._add(raw, null, cache);
+ 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
diff --git a/src/structures/Channel.js b/src/structures/Channel.js
index 6dbcaa93214a..596892345f9e 100644
--- a/src/structures/Channel.js
+++ b/src/structures/Channel.js
@@ -126,7 +126,7 @@ class Channel extends Base {
return ThreadChannelTypes.includes(this.type);
}
- static create(client, data, guild, allowUnknownGuild) {
+ static create(client, data, guild, { allowUnknownGuild, fromInteraction }) {
if (!CategoryChannel) CategoryChannel = require('./CategoryChannel');
if (!DMChannel) DMChannel = require('./DMChannel');
if (!NewsChannel) NewsChannel = require('./NewsChannel');
@@ -176,7 +176,7 @@ class Channel extends Base {
case ChannelTypes.GUILD_NEWS_THREAD:
case ChannelTypes.GUILD_PUBLIC_THREAD:
case ChannelTypes.GUILD_PRIVATE_THREAD: {
- channel = new ThreadChannel(guild, data, client);
+ channel = new ThreadChannel(guild, data, client, fromInteraction);
if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel);
break;
}
diff --git a/src/structures/CommandInteraction.js b/src/structures/CommandInteraction.js
index cc546d6353a8..6d839e735b03 100644
--- a/src/structures/CommandInteraction.js
+++ b/src/structures/CommandInteraction.js
@@ -121,7 +121,9 @@ class CommandInteraction extends Interaction {
if (member) result.member = this.guild?.members._add({ user, ...member }) ?? member;
const channel = resolved.channels?.[option.value];
- if (channel) result.channel = this.client.channels._add(channel, this.guild) ?? channel;
+ if (channel) {
+ result.channel = this.client.channels._add(channel, this.guild, { fromInteraction: true }) ?? channel;
+ }
const role = resolved.roles?.[option.value];
if (role) result.role = this.guild?.roles._add(role) ?? role;
diff --git a/src/structures/Invite.js b/src/structures/Invite.js
index f325eceef754..95bad22b21eb 100644
--- a/src/structures/Invite.js
+++ b/src/structures/Invite.js
@@ -109,7 +109,7 @@ class Invite extends Base {
* The channel the invite is for
* @type {Channel}
*/
- this.channel = this.client.channels._add(data.channel, this.guild, false);
+ this.channel = this.client.channels._add(data.channel, this.guild, { cache: false });
/**
* The timestamp the invite was created at
diff --git a/src/structures/ThreadChannel.js b/src/structures/ThreadChannel.js
index aad85652c23a..8c411daed902 100644
--- a/src/structures/ThreadChannel.js
+++ b/src/structures/ThreadChannel.js
@@ -16,8 +16,9 @@ class ThreadChannel extends Channel {
* @param {Guild} guild The guild the thread channel is part of
* @param {APIChannel} data The data for the thread channel
* @param {Client} [client] A safety parameter for the client that instantiated this
+ * @param {boolean} [fromInteraction=false] Whether the data was from an interaction (partial)
*/
- constructor(guild, data, client) {
+ constructor(guild, data, client, fromInteraction = false) {
super(guild?.client ?? client, data, false);
/**
@@ -43,10 +44,10 @@ class ThreadChannel extends Channel {
* @type {ThreadMemberManager}
*/
this.members = new ThreadMemberManager(this);
- if (data) this._patch(data);
+ if (data) this._patch(data, fromInteraction);
}
- _patch(data) {
+ _patch(data, partial = false) {
super._patch(data);
/**
@@ -62,27 +63,29 @@ class ThreadChannel extends Channel {
if ('parent_id' in data) {
/**
* The id of the parent channel of this thread
- * @type {Snowflake}
+ * @type {?Snowflake}
*/
this.parentId = data.parent_id;
+ } else if (!this.parentId) {
+ this.parentId = null;
}
if ('thread_metadata' in data) {
/**
* Whether the thread is locked
- * @type {boolean}
+ * @type {?boolean}
*/
this.locked = data.thread_metadata.locked ?? false;
/**
* Whether the thread is archived
- * @type {boolean}
+ * @type {?boolean}
*/
this.archived = data.thread_metadata.archived;
/**
* The amount of time (in minutes) after which the thread will automatically archive in case of no recent activity
- * @type {number}
+ * @type {?number}
*/
this.autoArchiveDuration = data.thread_metadata.auto_archive_duration;
@@ -90,9 +93,22 @@ class ThreadChannel extends Channel {
* The timestamp when the thread's archive status was last changed
* If the thread was never archived or unarchived, this is the timestamp at which the thread was
* created
- * @type {number}
+ * @type {?number}
*/
this.archiveTimestamp = new Date(data.thread_metadata.archive_timestamp).getTime();
+ } else {
+ if (!this.locked) {
+ this.locked = null;
+ }
+ if (!this.archived) {
+ this.archived = null;
+ }
+ if (!this.autoArchiveDuration) {
+ this.autoArchiveDuration = null;
+ }
+ if (!this.archiveTimestamp) {
+ this.archiveTimestamp = null;
+ }
}
if ('owner_id' in data) {
@@ -101,6 +117,8 @@ class ThreadChannel extends Channel {
* @type {?Snowflake}
*/
this.ownerId = data.owner_id;
+ } else if (!this.ownerId) {
+ this.ownerId = null;
}
if ('last_message_id' in data) {
@@ -109,6 +127,8 @@ class ThreadChannel extends Channel {
* @type {?Snowflake}
*/
this.lastMessageId = data.last_message_id;
+ } else if (!this.lastMessageId) {
+ this.lastMessageId = null;
}
if ('last_pin_timestamp' in data) {
@@ -117,14 +137,18 @@ class ThreadChannel extends Channel {
* @type {?number}
*/
this.lastPinTimestamp = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
+ } else if (!this.lastPinTimestamp) {
+ this.lastPinTimestamp = null;
}
- if ('rate_limit_per_user' in data) {
+ if ('rate_limit_per_user' in data || !partial) {
/**
* The ratelimit per user for this thread (in seconds)
- * @type {number}
+ * @type {?number}
*/
this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
+ } else if (!this.rateLimitPerUser) {
+ this.rateLimitPerUser = null;
}
if ('message_count' in data) {
@@ -132,9 +156,11 @@ class ThreadChannel extends Channel {
* The approximate count of messages in this thread
* This stops counting at 50. If you need an approximate value higher than that, use
* `ThreadChannel#messages.cache.size`
- * @type {number}
+ * @type {?number}
*/
this.messageCount = data.message_count;
+ } else if (!this.messageCount) {
+ this.messageCount = null;
}
if ('member_count' in data) {
@@ -142,9 +168,11 @@ class ThreadChannel extends Channel {
* The approximate count of users in this thread
* This stops counting at 50. If you need an approximate value higher than that, use
* `ThreadChannel#members.cache.size`
- * @type {number}
+ * @type {?number}
*/
this.memberCount = data.member_count;
+ } else if (!this.memberCount) {
+ this.memberCount = null;
}
if (data.member && this.client.user) this.members._add({ user_id: this.client.user.id, ...data.member });
@@ -163,10 +191,11 @@ class ThreadChannel extends Channel {
/**
* The time at which this thread's archive status was last changed
* If the thread was never archived or unarchived, this is the time at which the thread was created
- * @type {Date}
+ * @type {?Date}
* @readonly
*/
get archivedAt() {
+ if (!this.archiveTimestamp) return null;
return new Date(this.archiveTimestamp);
}
diff --git a/typings/index.d.ts b/typings/index.d.ts
index c99166224e38..75619ffca297 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -1660,18 +1660,18 @@ export class TextChannel extends TextBasedChannel(GuildChannel) {
}
export class ThreadChannel extends TextBasedChannel(Channel) {
- public constructor(guild: Guild, data?: object, client?: Client);
- public archived: boolean;
- public readonly archivedAt: Date;
- public archiveTimestamp: number;
- public autoArchiveDuration: ThreadAutoArchiveDuration;
+ public constructor(guild: Guild, data?: object, client?: Client, fromInteraction?: boolean);
+ public archived: boolean | null;
+ public readonly archivedAt: Date | null;
+ public archiveTimestamp: number | null;
+ public autoArchiveDuration: ThreadAutoArchiveDuration | null;
public readonly editable: boolean;
public guild: Guild;
public guildId: Snowflake;
public readonly guildMembers: Collection;
public readonly joinable: boolean;
public readonly joined: boolean;
- public locked: boolean;
+ public locked: boolean | null;
public readonly manageable: boolean;
public readonly sendable: boolean;
public memberCount: number | null;
@@ -1679,10 +1679,10 @@ export class ThreadChannel extends TextBasedChannel(Channel) {
public messages: MessageManager;
public members: ThreadMemberManager;
public name: string;
- public ownerId: Snowflake;
+ public ownerId: Snowflake | null;
public readonly parent: TextChannel | NewsChannel | null;
- public parentId: Snowflake;
- public rateLimitPerUser: number;
+ public parentId: Snowflake | null;
+ public rateLimitPerUser: number | null;
public type: ThreadChannelTypes;
public readonly unarchivable: boolean;
public delete(reason?: string): Promise;