From d5c9546e7f6e94635b0c22489e3b874bda26a9f5 Mon Sep 17 00:00:00 2001 From: ckohen Date: Sun, 31 Aug 2025 17:26:03 -0700 Subject: [PATCH 1/4] fix(guildChannel): better channel editing --- .../src/managers/GuildChannelManager.js | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index 628f0603b7ad..f2eab26f8452 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -12,6 +12,7 @@ const { ChannelFlagsBitField } = require('../util/ChannelFlagsBitField.js'); const { transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Channels.js'); const { ThreadChannelTypes } = require('../util/Constants.js'); const { resolveImage } = require('../util/DataResolver.js'); +const { toSnakeCase } = require('../util/Transformers.js'); const { setPosition } = require('../util/Util.js'); const { CachedManager } = require('./CachedManager.js'); const { GuildTextThreadManager } = require('./GuildTextThreadManager.js'); @@ -298,7 +299,7 @@ class GuildChannelManager extends CachedManager { /** * Edits the channel. * - * @param {GuildChannelResolvable} channel The channel to edit + * @param {GuildChannelResolvable} channel The channel to edit. Can be the id of an uncached channel if not editing position or locking permissions * @param {GuildChannelEditOptions} options Options for editing the channel * @returns {Promise} * @example @@ -308,13 +309,17 @@ class GuildChannelManager extends CachedManager { * .catch(console.error); */ async edit(channel, options) { - const resolvedChannel = this.resolve(channel); - if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); + // Let uncached channels be edited if they don't need to lock to a parent without passing parent_id + const resolvedChannelId = this.resolveId(channel); + if (!resolvedChannelId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); const parentId = options.parent && this.client.channels.resolveId(options.parent); if (options.position !== undefined) { - await this.setPosition(resolvedChannel, options.position, { position: options.position, reason: options.reason }); + await this.setPosition(resolvedChannelId, options.position, { + position: options.position, + reason: options.reason, + }); } let permission_overwrites = options.permissionOverwrites?.map(overwrite => @@ -329,36 +334,53 @@ class GuildChannelManager extends CachedManager { PermissionOverwrites.resolve(overwrite, this.guild), ); } - } else if (resolvedChannel.parent) { - permission_overwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite => - PermissionOverwrites.resolve(overwrite, this.guild), - ); + } else { + const resolvedChannel = this.resolve(channel); + if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); + if (resolvedChannel.parent) { + permission_overwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite => + PermissionOverwrites.resolve(overwrite, this.guild), + ); + } } } - const newData = await this.client.rest.patch(Routes.channel(resolvedChannel.id), { - body: { - name: options.name, - type: options.type, - topic: options.topic, - nsfw: options.nsfw, - bitrate: options.bitrate, - user_limit: options.userLimit, - rtc_region: options.rtcRegion, - video_quality_mode: options.videoQualityMode, - parent_id: parentId, - lock_permissions: options.lockPermissions, - rate_limit_per_user: options.rateLimitPerUser, - default_auto_archive_duration: options.defaultAutoArchiveDuration, - permission_overwrites, - available_tags: options.availableTags?.map(availableTag => transformGuildForumTag(availableTag)), - default_reaction_emoji: - options.defaultReactionEmoji && transformGuildDefaultReaction(options.defaultReactionEmoji), - default_thread_rate_limit_per_user: options.defaultThreadRateLimitPerUser, - flags: 'flags' in options ? ChannelFlagsBitField.resolve(options.flags) : undefined, - default_sort_order: options.defaultSortOrder, - default_forum_layout: options.defaultForumLayout, + const snakeCaseBody = Object.assign( + // Remove properties we don't want to pass to the API in the body + { + set parent(_) {}, + set position(_) {}, + set reason(_) {}, }, + toSnakeCase(options), + ); + + // This overwrites a passed snake_case version if a camelCase version OR `parent` is passed + if (parentId) { + snakeCaseBody.parent_id = parentId; + } + + // This overwrites a passed snake_case version if a camelCase version is passed + if (permission_overwrites) { + snakeCaseBody.permission_overwrites = permission_overwrites; + } + + if (snakeCaseBody.available_tags) { + snakeCaseBody.available_tags = snakeCaseBody.available_tags.map(availableTag => + 'emoji' in availableTag ? transformGuildForumTag(availableTag) : availableTag, + ); + } + + if (snakeCaseBody.default_reaction_emoji?.id) { + snakeCaseBody.default_reaction_emoji = transformGuildDefaultReaction(snakeCaseBody.default_reaction_emoji); + } + + if (snakeCaseBody.flags) { + snakeCaseBody.flags = ChannelFlagsBitField.resolve(snakeCaseBody.flags); + } + + const newData = await this.client.rest.patch(Routes.channel(resolvedChannelId), { + body: snakeCaseBody, reason: options.reason, }); From 67f3c8b079159b4ebf1bf15d74a3e9b8fb9a09ba Mon Sep 17 00:00:00 2001 From: Chai Kohen Date: Thu, 4 Sep 2025 19:51:26 -0700 Subject: [PATCH 2/4] fix: variable casing --- .../discord.js/src/managers/GuildChannelManager.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index f2eab26f8452..17603bb0c442 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -322,7 +322,7 @@ class GuildChannelManager extends CachedManager { }); } - let permission_overwrites = options.permissionOverwrites?.map(overwrite => + let permissionOverwrites = options.permissionOverwrites?.map(overwrite => PermissionOverwrites.resolve(overwrite, this.guild), ); @@ -330,7 +330,7 @@ class GuildChannelManager extends CachedManager { if (parentId) { const newParent = this.cache.get(parentId); if (newParent?.type === ChannelType.GuildCategory) { - permission_overwrites = newParent.permissionOverwrites.cache.map(overwrite => + permissionOverwrites = newParent.permissionOverwrites.cache.map(overwrite => PermissionOverwrites.resolve(overwrite, this.guild), ); } @@ -338,7 +338,7 @@ class GuildChannelManager extends CachedManager { const resolvedChannel = this.resolve(channel); if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); if (resolvedChannel.parent) { - permission_overwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite => + permissionOverwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite => PermissionOverwrites.resolve(overwrite, this.guild), ); } @@ -361,8 +361,8 @@ class GuildChannelManager extends CachedManager { } // This overwrites a passed snake_case version if a camelCase version is passed - if (permission_overwrites) { - snakeCaseBody.permission_overwrites = permission_overwrites; + if (permissionOverwrites) { + snakeCaseBody.permission_overwrites = permissionOverwrites; } if (snakeCaseBody.available_tags) { From 4d10c8bf4762df8987385851ae081e8817ec1ea9 Mon Sep 17 00:00:00 2001 From: ckohen Date: Fri, 12 Sep 2025 02:39:55 -0700 Subject: [PATCH 3/4] fix: default reaction emoji can be name-only --- packages/discord.js/src/managers/GuildChannelManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index 17603bb0c442..4981bec8a0fe 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -371,7 +371,7 @@ class GuildChannelManager extends CachedManager { ); } - if (snakeCaseBody.default_reaction_emoji?.id) { + if (snakeCaseBody.default_reaction_emoji?.id || snakeCaseBody.default_reaction_emoji?.name) { snakeCaseBody.default_reaction_emoji = transformGuildDefaultReaction(snakeCaseBody.default_reaction_emoji); } From 077462fa46d54e8b9f3cb7985c78501d88c9dbb5 Mon Sep 17 00:00:00 2001 From: Chai Kohen Date: Sun, 12 Oct 2025 13:24:02 -0700 Subject: [PATCH 4/4] fix: use passed options when converting --- .../discord.js/src/managers/GuildChannelManager.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index 4981bec8a0fe..4ec014c8ddce 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -365,18 +365,18 @@ class GuildChannelManager extends CachedManager { snakeCaseBody.permission_overwrites = permissionOverwrites; } - if (snakeCaseBody.available_tags) { - snakeCaseBody.available_tags = snakeCaseBody.available_tags.map(availableTag => + if (options.availableTags) { + snakeCaseBody.available_tags = options.availableTags.map(availableTag => 'emoji' in availableTag ? transformGuildForumTag(availableTag) : availableTag, ); } - if (snakeCaseBody.default_reaction_emoji?.id || snakeCaseBody.default_reaction_emoji?.name) { - snakeCaseBody.default_reaction_emoji = transformGuildDefaultReaction(snakeCaseBody.default_reaction_emoji); + if (options.defaultReactionEmoji?.id || options.DefaultReactionEmoji?.name) { + snakeCaseBody.default_reaction_emoji = transformGuildDefaultReaction(options.defaultReactionEmoji); } - if (snakeCaseBody.flags) { - snakeCaseBody.flags = ChannelFlagsBitField.resolve(snakeCaseBody.flags); + if (options.flags) { + snakeCaseBody.flags = ChannelFlagsBitField.resolve(options.flags); } const newData = await this.client.rest.patch(Routes.channel(resolvedChannelId), {