Skip to content

Commit 755c180

Browse files
ckohenkyranet
andauthored
feat: allow channels from uncached guilds to be returned from fetch (#6034)
Co-authored-by: Antonio Román <[email protected]>
1 parent 2e078e4 commit 755c180

File tree

9 files changed

+79
-34
lines changed

9 files changed

+79
-34
lines changed

src/errors/Messages.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const Messages = {
9090
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
9191
GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.',
9292
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
93+
GUILD_CHANNEL_UNOWNED: "The fetched channel does not belong to this manager's guild.",
9394
GUILD_OWNED: 'Guild is owned by the client.',
9495
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
9596
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',

src/managers/ChannelManager.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ChannelManager extends CachedManager {
1919
* @name ChannelManager#cache
2020
*/
2121

22-
add(data, guild, cache = true) {
22+
add(data, guild, cache = true, allowUnknownGuild = false) {
2323
const existing = this.cache.get(data.id);
2424
if (existing) {
2525
if (cache) existing._patch(data);
@@ -30,14 +30,14 @@ class ChannelManager extends CachedManager {
3030
return existing;
3131
}
3232

33-
const channel = Channel.create(this.client, data, guild);
33+
const channel = Channel.create(this.client, data, guild, allowUnknownGuild);
3434

3535
if (!channel) {
3636
this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`);
3737
return null;
3838
}
3939

40-
if (cache) this.cache.set(channel.id, channel);
40+
if (cache && !allowUnknownGuild) this.cache.set(channel.id, channel);
4141

4242
return channel;
4343
}
@@ -74,25 +74,32 @@ class ChannelManager extends CachedManager {
7474
* @returns {?Snowflake}
7575
*/
7676

77+
/**
78+
* Options for fetching a channel from discord
79+
* @typedef {BaseFetchOptions} FetchChannelOptions
80+
* @property {boolean} [allowUnknownGuild=false] Allows the channel to be returned even if the guild is not in cache,
81+
* it will not be cached. <warn>Many of the properties and methods on the returned channel will throw errors</warn>
82+
*/
83+
7784
/**
7885
* Obtains a channel from Discord, or the channel cache if it's already available.
7986
* @param {Snowflake} id The channel's id
80-
* @param {BaseFetchOptions} [options] Additional options for this fetch
87+
* @param {FetchChannelOptions} [options] Additional options for this fetch
8188
* @returns {Promise<?Channel>}
8289
* @example
8390
* // Fetch a channel by its id
8491
* client.channels.fetch('222109930545610754')
8592
* .then(channel => console.log(channel.name))
8693
* .catch(console.error);
8794
*/
88-
async fetch(id, { cache = true, force = false } = {}) {
95+
async fetch(id, { allowUnknownGuild = false, cache = true, force = false } = {}) {
8996
if (!force) {
9097
const existing = this.cache.get(id);
9198
if (existing && !existing.partial) return existing;
9299
}
93100

94101
const data = await this.client.api.channels(id).get();
95-
return this.add(data, null, cache);
102+
return this.add(data, null, cache, allowUnknownGuild);
96103
}
97104
}
98105

src/managers/GuildChannelManager.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const CachedManager = require('./CachedManager');
4+
const { Error } = require('../errors');
45
const GuildChannel = require('../structures/GuildChannel');
56
const PermissionOverwrites = require('../structures/PermissionOverwrites');
67
const ThreadChannel = require('../structures/ThreadChannel');
@@ -164,11 +165,17 @@ class GuildChannelManager extends CachedManager {
164165
if (existing) return existing;
165166
}
166167

167-
// We cannot fetch a single guild channel, as of this commit's date, Discord API throws with 404
168+
if (id) {
169+
const data = await this.client.api.channels(id).get();
170+
// Since this is the guild manager, throw if on a different guild
171+
if (this.guild.id !== data.guild_id) throw new Error('GUILD_CHANNEL_UNOWNED');
172+
return this.client.channels.add(data, this.guild, cache);
173+
}
174+
168175
const data = await this.client.api.guilds(this.guild.id).channels.get();
169176
const channels = new Collection();
170177
for (const channel of data) channels.set(channel.id, this.client.channels.add(channel, this.guild, cache));
171-
return id ? channels.get(id) ?? null : channels;
178+
return channels;
172179
}
173180
}
174181

src/structures/Channel.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class Channel extends Base {
127127
return ThreadChannelTypes.includes(this.type);
128128
}
129129

130-
static create(client, data, guild) {
130+
static create(client, data, guild, allowUnknownGuild) {
131131
if (!CategoryChannel) CategoryChannel = require('./CategoryChannel');
132132
if (!DMChannel) DMChannel = require('./DMChannel');
133133
if (!NewsChannel) NewsChannel = require('./NewsChannel');
@@ -148,41 +148,41 @@ class Channel extends Base {
148148
} else {
149149
if (!guild) guild = client.guilds.cache.get(data.guild_id);
150150

151-
if (guild) {
151+
if (guild || allowUnknownGuild) {
152152
switch (data.type) {
153153
case ChannelTypes.TEXT: {
154-
channel = new TextChannel(guild, data);
154+
channel = new TextChannel(guild, data, client);
155155
break;
156156
}
157157
case ChannelTypes.VOICE: {
158-
channel = new VoiceChannel(guild, data);
158+
channel = new VoiceChannel(guild, data, client);
159159
break;
160160
}
161161
case ChannelTypes.CATEGORY: {
162-
channel = new CategoryChannel(guild, data);
162+
channel = new CategoryChannel(guild, data, client);
163163
break;
164164
}
165165
case ChannelTypes.NEWS: {
166-
channel = new NewsChannel(guild, data);
166+
channel = new NewsChannel(guild, data, client);
167167
break;
168168
}
169169
case ChannelTypes.STORE: {
170-
channel = new StoreChannel(guild, data);
170+
channel = new StoreChannel(guild, data, client);
171171
break;
172172
}
173173
case ChannelTypes.STAGE: {
174-
channel = new StageChannel(guild, data);
174+
channel = new StageChannel(guild, data, client);
175175
break;
176176
}
177177
case ChannelTypes.NEWS_THREAD:
178178
case ChannelTypes.PUBLIC_THREAD:
179179
case ChannelTypes.PRIVATE_THREAD: {
180-
channel = new ThreadChannel(guild, data);
181-
channel.parent?.threads.cache.set(channel.id, channel);
180+
channel = new ThreadChannel(guild, data, client);
181+
if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel);
182182
break;
183183
}
184184
}
185-
if (channel) guild.channels?.cache.set(channel.id, channel);
185+
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
186186
}
187187
}
188188
return channel;

src/structures/GuildChannel.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,23 @@ class GuildChannel extends Channel {
2424
/**
2525
* @param {Guild} guild The guild the guild channel is part of
2626
* @param {APIChannel} data The data for the guild channel
27+
* @param {Client} [client] A safety parameter for the client that instantiated this
2728
*/
28-
constructor(guild, data) {
29-
super(guild.client, data, false);
29+
constructor(guild, data, client) {
30+
super(guild?.client ?? client, data, false);
3031

3132
/**
3233
* The guild the channel is in
3334
* @type {Guild}
3435
*/
3536
this.guild = guild;
3637

38+
/**
39+
* The id of the guild the channel is in
40+
* @type {Snowflake}
41+
*/
42+
this.guildId = guild?.id ?? data.guild_id;
43+
3744
this.parentId = this.parentId ?? null;
3845
/**
3946
* A manager of permission overwrites that belong to this channel
@@ -63,6 +70,10 @@ class GuildChannel extends Channel {
6370
this.rawPosition = data.position;
6471
}
6572

73+
if ('guild_id' in data) {
74+
this.guildId = data.guild_id;
75+
}
76+
6677
if ('parent_id' in data) {
6778
/**
6879
* The id of the category parent of this channel

src/structures/StoreChannel.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ const GuildChannel = require('./GuildChannel');
88
*/
99
class StoreChannel extends GuildChannel {
1010
/**
11-
* @param {*} guild The guild the store channel is part of
12-
* @param {*} data The data for the store channel
11+
* @param {Guild} guild The guild the store channel is part of
12+
* @param {APIChannel} data The data for the store channel
13+
* @param {Client} [client] A safety parameter for the client that instantiated this
1314
*/
14-
constructor(guild, data) {
15-
super(guild, data);
15+
constructor(guild, data, client) {
16+
super(guild, data, client);
1617

1718
/**
1819
* If the guild considers this channel NSFW

src/structures/TextChannel.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ class TextChannel extends GuildChannel {
1717
/**
1818
* @param {Guild} guild The guild the text channel is part of
1919
* @param {APIChannel} data The data for the text channel
20+
* @param {Client} [client] A safety parameter for the client that instantiated this
2021
*/
21-
constructor(guild, data) {
22-
super(guild, data);
22+
constructor(guild, data, client) {
23+
super(guild, data, client);
2324
/**
2425
* A manager of the messages sent to this channel
2526
* @type {MessageManager}

src/structures/ThreadChannel.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,23 @@ class ThreadChannel extends Channel {
1515
/**
1616
* @param {Guild} guild The guild the thread channel is part of
1717
* @param {APIChannel} data The data for the thread channel
18+
* @param {Client} [client] A safety parameter for the client that instantiated this
1819
*/
19-
constructor(guild, data) {
20-
super(guild.client, data, false);
20+
constructor(guild, data, client) {
21+
super(guild?.client ?? client, data, false);
2122

2223
/**
2324
* The guild the thread is in
2425
* @type {Guild}
2526
*/
2627
this.guild = guild;
2728

29+
/**
30+
* The id of the guild the channel is in
31+
* @type {Snowflake}
32+
*/
33+
this.guildId = guild?.id ?? data.guild_id;
34+
2835
/**
2936
* A manager of the messages sent to this thread
3037
* @type {MessageManager}
@@ -50,6 +57,10 @@ class ThreadChannel extends Channel {
5057
*/
5158
this.name = data.name;
5259

60+
if ('guild_id' in data) {
61+
this.guildId = data.guild_id;
62+
}
63+
5364
if ('parent_id' in data) {
5465
/**
5566
* The id of the parent channel of this thread

typings/index.d.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -619,13 +619,14 @@ export class GuildBan extends Base {
619619
}
620620

621621
export class GuildChannel extends Channel {
622-
public constructor(guild: Guild, data?: unknown);
622+
public constructor(guild: Guild, data?: unknown, client?: Client);
623623
private memberPermissions(member: GuildMember): Readonly<Permissions>;
624624
private rolePermissions(role: Role): Readonly<Permissions>;
625625

626626
public readonly calculatedPosition: number;
627627
public readonly deletable: boolean;
628628
public guild: Guild;
629+
public guildId: Snowflake;
629630
public readonly manageable: boolean;
630631
public readonly members: Collection<Snowflake, GuildMember>;
631632
public name: string;
@@ -1519,7 +1520,7 @@ export class Sticker extends Base {
15191520
}
15201521

15211522
export class StoreChannel extends GuildChannel {
1522-
public constructor(guild: Guild, data?: unknown);
1523+
public constructor(guild: Guild, data?: unknown, client?: Client);
15231524
public nsfw: boolean;
15241525
public type: 'store';
15251526
}
@@ -1558,7 +1559,7 @@ export class TeamMember extends Base {
15581559
}
15591560

15601561
export class TextChannel extends TextBasedChannel(GuildChannel) {
1561-
public constructor(guild: Guild, data?: unknown);
1562+
public constructor(guild: Guild, data?: unknown, client?: Client);
15621563
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
15631564
public messages: MessageManager;
15641565
public nsfw: boolean;
@@ -1578,13 +1579,14 @@ export class TextChannel extends TextBasedChannel(GuildChannel) {
15781579
}
15791580

15801581
export class ThreadChannel extends TextBasedChannel(Channel) {
1581-
public constructor(guild: Guild, data?: object);
1582+
public constructor(guild: Guild, data?: object, client?: Client);
15821583
public archived: boolean;
15831584
public readonly archivedAt: Date;
15841585
public archiveTimestamp: number;
15851586
public autoArchiveDuration: ThreadAutoArchiveDuration;
15861587
public readonly editable: boolean;
15871588
public guild: Guild;
1589+
public guildId: Snowflake;
15881590
public readonly guildMembers: Collection<Snowflake, GuildMember>;
15891591
public readonly joinable: boolean;
15901592
public readonly joined: boolean;
@@ -2266,7 +2268,7 @@ export class BaseGuildEmojiManager extends CachedManager<Snowflake, GuildEmoji,
22662268

22672269
export class ChannelManager extends CachedManager<Snowflake, Channel, ChannelResolvable> {
22682270
public constructor(client: Client, iterable: Iterable<unknown>);
2269-
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<Channel | null>;
2271+
public fetch(id: Snowflake, options?: FetchChannelOptions): Promise<Channel | null>;
22702272
}
22712273

22722274
export class GuildApplicationCommandManager extends ApplicationCommandManager<ApplicationCommand, {}, Guild> {
@@ -3158,6 +3160,10 @@ export interface FetchBansOptions {
31583160
cache: boolean;
31593161
}
31603162

3163+
export interface FetchChannelOptions extends BaseFetchOptions {
3164+
allowUnknownGuild?: boolean;
3165+
}
3166+
31613167
export interface FetchedThreads {
31623168
threads: Collection<Snowflake, ThreadChannel>;
31633169
hasMore?: boolean;

0 commit comments

Comments
 (0)