diff --git a/src/managers/GuildChannelManager.js b/src/managers/GuildChannelManager.js index 687816f97ab6..75eebde0d9af 100644 --- a/src/managers/GuildChannelManager.js +++ b/src/managers/GuildChannelManager.js @@ -4,11 +4,14 @@ const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const CachedManager = require('./CachedManager'); const ThreadManager = require('./ThreadManager'); -const { Error } = require('../errors'); +const { Error, TypeError } = require('../errors'); const GuildChannel = require('../structures/GuildChannel'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const ThreadChannel = require('../structures/ThreadChannel'); -const { ChannelTypes, ThreadChannelTypes } = require('../util/Constants'); +const Webhook = require('../structures/Webhook'); +const { ThreadChannelTypes, ChannelTypes } = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Util = require('../util/Util'); let cacheWarningEmitted = false; let storeChannelDeprecationEmitted = false; @@ -169,6 +172,147 @@ class GuildChannelManager extends CachedManager { return this.client.actions.ChannelCreate.handle(data).channel; } + /** + * Creates a webhook for the channel. + * @param {GuildChannelResolvable} channel The channel to create the webhook for + * @param {string} name The name of the webhook + * @param {ChannelWebhookCreateOptions} [options] Options for creating the webhook + * @returns {Promise} Returns the created Webhook + * @example + * // Create a webhook for the current channel + * guild.channels.createWebhook('222197033908436994', 'Snek', { + * avatar: 'https://i.imgur.com/mI8XcpG.jpg', + * reason: 'Needed a cool new Webhook' + * }) + * .then(console.log) + * .catch(console.error) + */ + async createWebhook(channel, name, { avatar, reason } = {}) { + const id = this.resolveId(channel); + if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + if (typeof avatar === 'string' && !avatar.startsWith('data:')) { + avatar = await DataResolver.resolveImage(avatar); + } + const data = await this.client.api.channels[id].webhooks.post({ + data: { + name, + avatar, + }, + reason, + }); + return new Webhook(this.client, data); + } + + /** + * The data for a guild channel. + * @typedef {Object} ChannelData + * @property {string} [name] The name of the channel + * @property {ChannelType} [type] The type of the channel (only conversion between text and news is supported) + * @property {number} [position] The position of the channel + * @property {string} [topic] The topic of the text channel + * @property {boolean} [nsfw] Whether the channel is NSFW + * @property {number} [bitrate] The bitrate of the voice channel + * @property {number} [userLimit] The user limit of the voice channel + * @property {?CategoryChannelResolvable} [parent] The parent of the channel + * @property {boolean} [lockPermissions] + * Lock the permissions of the channel to what the parent's permissions are + * @property {OverwriteResolvable[]|Collection} [permissionOverwrites] + * Permission overwrites for the channel + * @property {number} [rateLimitPerUser] The rate limit per user (slowmode) 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 + */ + + /** + * Edits the channel. + * @param {GuildChannelResolvable} channel The channel to edit + * @param {ChannelData} data The new data for the channel + * @param {string} [reason] Reason for editing this channel + * @returns {Promise} + * @example + * // Edit a channel + * guild.channels.edit('222197033908436994', { name: 'new-channel' }) + * .then(console.log) + * .catch(console.error); + */ + async edit(channel, data, reason) { + channel = this.resolve(channel); + if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + + const parent = data.parent && this.client.channels.resolveId(data.parent); + + if (typeof data.position !== 'undefined') await this.setPosition(channel, data.position, { reason }); + + let permission_overwrites = data.permissionOverwrites?.map(o => PermissionOverwrites.resolve(o, this.guild)); + + if (data.lockPermissions) { + if (parent) { + const newParent = this.guild.channels.resolve(parent); + if (newParent?.type === 'GUILD_CATEGORY') { + permission_overwrites = newParent.permissionOverwrites.cache.map(o => + PermissionOverwrites.resolve(o, this.guild), + ); + } + } else if (channel.parent) { + permission_overwrites = this.parent.permissionOverwrites.cache.map(o => + PermissionOverwrites.resolve(o, this.guild), + ); + } + } + + const newData = await this.client.api.channels(channel.id).patch({ + data: { + name: (data.name ?? channel.name).trim(), + type: data.type, + topic: data.topic, + nsfw: data.nsfw, + bitrate: data.bitrate ?? channel.bitrate, + user_limit: data.userLimit ?? channel.userLimit, + rtc_region: data.rtcRegion ?? channel.rtcRegion, + parent_id: parent, + lock_permissions: data.lockPermissions, + rate_limit_per_user: data.rateLimitPerUser, + default_auto_archive_duration: data.defaultAutoArchiveDuration, + permission_overwrites, + }, + reason, + }); + + return this.client.actions.ChannelUpdate.handle(newData).updated; + } + + /** + * Sets a new position for the guild channel. + * @param {GuildChannelResolvable} channel The channel to set the position for + * @param {number} position The new position for the guild channel + * @param {SetChannelPositionOptions} [options] Options for setting position + * @returns {Promise} + * @example + * // Set a new channel position + * guild.channels.setPosition('222078374472843266', 2) + * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) + * .catch(console.error); + */ + async setPosition(channel, position, { relative, reason } = {}) { + channel = this.resolve(channel); + if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + const updatedChannels = await Util.setPosition( + channel, + position, + relative, + this.guild._sortedChannels(this), + this.client.api.guilds(this.guild.id).channels, + reason, + ); + + this.client.actions.GuildChannelsPositionUpdate.handle({ + guild_id: this.guild.id, + channels: updatedChannels, + }); + return channel; + } + /** * Obtains one or more guild channels from Discord, or the channel cache if they're already available. * @param {Snowflake} [id] The channel's id @@ -204,6 +348,39 @@ class GuildChannelManager extends CachedManager { return channels; } + /** + * Fetches all webhooks for the channel. + * @param {GuildChannelResolvable} channel The channel to fetch webhooks for + * @returns {Promise>} + * @example + * // Fetch webhooks + * guild.channels.fetchWebhooks('769862166131245066') + * .then(hooks => console.log(`This channel has ${hooks.size} hooks`)) + * .catch(console.error); + */ + async fetchWebhooks(channel) { + const id = this.resolveId(channel); + if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + const data = await this.client.api.channels[id].webhooks.get(); + return data.reduce((hooks, hook) => hooks.set(hook.id, new Webhook(this.client, hook)), new Collection()); + } + + /** + * Data that can be resolved to give a Category Channel object. This can be: + * * A CategoryChannel object + * * A Snowflake + * @typedef {CategoryChannel|Snowflake} CategoryChannelResolvable + */ + + /** + * The data needed for updating a channel's position. + * @typedef {Object} ChannelPosition + * @property {GuildChannel|Snowflake} channel Channel to update + * @property {number} [position] New position for the channel + * @property {CategoryChannelResolvable} [parent] Parent channel for this channel + * @property {boolean} [lockPermissions] If the overwrites should be locked to the parents overwrites + */ + /** * Batch-updates the guild's channels' positions. * Only one channel's parent can be changed at a time @@ -243,6 +420,23 @@ class GuildChannelManager extends CachedManager { const raw = await this.client.api.guilds(this.guild.id).threads.active.get(); return ThreadManager._mapThreads(raw, this.client, { guild: this.guild, cache }); } + + /** + * Deletes the channel. + * @param {GuildChannelResolvable} channel The channel to delete + * @param {string} [reason] Reason for deleting this channel + * @returns {Promise} + * @example + * // Delete the channel + * guild.channels.delete('858850993013260338', 'making room for new channels') + * .then(console.log) + * .catch(console.error); + */ + async delete(channel, reason) { + const id = this.resolveId(channel); + if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable'); + await this.client.api.channels(this.id).delete({ reason }); + } } module.exports = GuildChannelManager; diff --git a/src/managers/GuildEmojiManager.js b/src/managers/GuildEmojiManager.js index 58ffa62425bb..74661f28019d 100644 --- a/src/managers/GuildEmojiManager.js +++ b/src/managers/GuildEmojiManager.js @@ -2,8 +2,9 @@ const { Collection } = require('@discordjs/collection'); const BaseGuildEmojiManager = require('./BaseGuildEmojiManager'); -const { TypeError } = require('../errors'); +const { Error, TypeError } = require('../errors'); const DataResolver = require('../util/DataResolver'); +const Permissions = require('../util/Permissions'); /** * Manages API methods for GuildEmojis and stores their cache. @@ -100,6 +101,71 @@ class GuildEmojiManager extends BaseGuildEmojiManager { for (const emoji of data) emojis.set(emoji.id, this._add(emoji, cache)); return emojis; } + + /** + * Deletes an emoji. + * @param {EmojiResolvable} emoji The Emoji resolvable to delete + * @param {string} [reason] Reason for deleting the emoji + * @returns {Promise} + */ + async delete(emoji, reason) { + const id = this.resolveId(emoji); + if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + await this.client.api.guilds(this.guild.id).emojis(id).delete({ reason }); + } + + /** + * Edits an emoji. + * @param {EmojiResolvable} emoji The Emoji resolvable to edit + * @param {GuildEmojiEditData} data The new data for the emoji + * @param {string} [reason] Reason for editing this emoji + * @returns {Promise} + */ + async edit(emoji, data, reason) { + const id = this.resolveId(emoji); + if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + const roles = data.roles?.map(r => this.guild.roles.resolveId(r)); + const newData = await this.client.api + .guilds(this.guild.id) + .emojis(id) + .patch({ + data: { + name: data.name, + roles, + }, + reason, + }); + const existing = this.cache.get(id); + if (existing) { + const clone = existing._clone(); + clone._patch(newData); + return clone; + } + return this._add(newData); + } + + /** + * Fetches the author for this emoji + * @param {EmojiResolvable} emoji The emoji to fetch the author of + * @returns {Promise} + */ + async fetchAuthor(emoji) { + emoji = this.resolve(emoji); + if (!emoji) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true); + if (emoji.managed) { + throw new Error('EMOJI_MANAGED'); + } + + const { me } = this.guild; + if (!me) throw new Error('GUILD_UNCACHED_ME'); + if (!me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS_AND_STICKERS)) { + throw new Error('MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION', this.guild); + } + + const data = await this.client.api.guilds(this.guild.id).emojis(emoji.id).get(); + emoji._patch(data); + return emoji.author; + } } module.exports = GuildEmojiManager; diff --git a/src/managers/GuildStickerManager.js b/src/managers/GuildStickerManager.js index 68abfb03b3bb..4897b027f337 100644 --- a/src/managers/GuildStickerManager.js +++ b/src/managers/GuildStickerManager.js @@ -161,6 +161,19 @@ class GuildStickerManager extends CachedManager { const data = await this.client.api.guilds(this.guild.id).stickers.get(); return new Collection(data.map(sticker => [sticker.id, this._add(sticker, cache)])); } + + /** + * Fetches the user who uploaded this sticker, if this is a guild sticker. + * @param {StickerResolvable} sticker The sticker to fetch the user for + * @returns {Promise} + */ + async fetchUser(sticker) { + sticker = this.resolve(sticker); + if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable'); + const data = await this.client.api.guilds(this.guildId).stickers(sticker.id).get(); + sticker._patch(data); + return sticker.user; + } } module.exports = GuildStickerManager; diff --git a/src/managers/RoleManager.js b/src/managers/RoleManager.js index ea690e41d91e..f3041b84e5fa 100644 --- a/src/managers/RoleManager.js +++ b/src/managers/RoleManager.js @@ -7,7 +7,8 @@ const { TypeError } = require('../errors'); const { Role } = require('../structures/Role'); const DataResolver = require('../util/DataResolver'); const Permissions = require('../util/Permissions'); -const { resolveColor, setPosition } = require('../util/Util'); +const { resolveColor } = require('../util/Util'); +const Util = require('../util/Util'); let cacheWarningEmitted = false; @@ -159,7 +160,7 @@ class RoleManager extends CachedManager { guild_id: this.guild.id, role: data, }); - if (position) return role.setPosition(position, reason); + if (position) return this.setPosition(role, position, { reason }); return role; } @@ -179,21 +180,7 @@ class RoleManager extends CachedManager { role = this.resolve(role); if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable'); - if (typeof data.position === 'number') { - const updatedRoles = await setPosition( - role, - data.position, - false, - this.guild._sortedRoles(), - this.client.api.guilds(this.guild.id).roles, - reason, - ); - - this.client.actions.GuildRolesPositionUpdate.handle({ - guild_id: this.guild.id, - roles: updatedRoles, - }); - } + if (typeof data.position === 'number') await this.setPosition(role, data.position, { reason }); let icon = data.icon; if (icon) { @@ -236,6 +223,44 @@ class RoleManager extends CachedManager { this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: id }); } + /** + * Sets the new position of the role. + * @param {RoleResolvable} role The role to change the position of + * @param {number} position The new position for the role + * @param {SetRolePositionOptions} [options] Options for setting the position + * @returns {Promise} + * @example + * // Set the position of the role + * guild.roles.setPosition('222197033908436994', 1) + * .then(updated => console.log(`Role position: ${updated.position}`)) + * .catch(console.error); + */ + async setPosition(role, position, { relative, reason } = {}) { + role = this.resolve(role); + if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable'); + const updatedRoles = await Util.setPosition( + role, + position, + relative, + this.guild._sortedRoles(), + this.client.api.guilds(this.guild.id).roles, + reason, + ); + + this.client.actions.GuildRolesPositionUpdate.handle({ + guild_id: this.guild.id, + roles: updatedRoles, + }); + return role; + } + + /** + * The data needed for updating a guild role's position + * @typedef {Object} GuildRolePosition + * @property {RoleResolvable} role The role's id + * @property {number} position The position to update + */ + /** * Batch-updates the guild's role positions * @param {GuildRolePosition[]} rolePositions Role positions to update diff --git a/src/structures/BaseGuildTextChannel.js b/src/structures/BaseGuildTextChannel.js index abb407563bde..7d2f5c0e04bb 100644 --- a/src/structures/BaseGuildTextChannel.js +++ b/src/structures/BaseGuildTextChannel.js @@ -1,12 +1,9 @@ 'use strict'; -const { Collection } = require('@discordjs/collection'); const GuildChannel = require('./GuildChannel'); -const Webhook = require('./Webhook'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const MessageManager = require('../managers/MessageManager'); const ThreadManager = require('../managers/ThreadManager'); -const DataResolver = require('../util/DataResolver'); /** * Represents a text-based guild channel on Discord. @@ -121,11 +118,8 @@ class BaseGuildTextChannel extends GuildChannel { * .then(hooks => console.log(`This channel has ${hooks.size} hooks`)) * .catch(console.error); */ - async fetchWebhooks() { - const data = await this.client.api.channels[this.id].webhooks.get(); - const hooks = new Collection(); - for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); - return hooks; + fetchWebhooks() { + return this.guild.channels.fetchWebhooks(this.id); } /** @@ -149,18 +143,8 @@ class BaseGuildTextChannel extends GuildChannel { * .then(console.log) * .catch(console.error) */ - async createWebhook(name, { avatar, reason } = {}) { - if (typeof avatar === 'string' && !avatar.startsWith('data:')) { - avatar = await DataResolver.resolveImage(avatar); - } - const data = await this.client.api.channels[this.id].webhooks.post({ - data: { - name, - avatar, - }, - reason, - }); - return new Webhook(this.client, data); + createWebhook(name, options = {}) { + return this.guild.channels.createWebhook(this.id, name, options); } /** @@ -178,6 +162,14 @@ class BaseGuildTextChannel extends GuildChannel { return this.edit({ topic }, reason); } + /** + * Data that can be resolved to an Application. This can be: + * * An Application + * * An Activity with associated Application + * * A Snowflake + * @typedef {Application|Snowflake} ApplicationResolvable + */ + /** * Options used to create an invite to a guild channel. * @typedef {Object} CreateInviteOptions diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index 03e86d3a429a..37dace27e34b 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -1,12 +1,10 @@ 'use strict'; const { Channel } = require('./Channel'); -const PermissionOverwrites = require('./PermissionOverwrites'); const { Error } = require('../errors'); const PermissionOverwriteManager = require('../managers/PermissionOverwriteManager'); -const { ChannelTypes, VoiceBasedChannelTypes } = require('../util/Constants'); +const { VoiceBasedChannelTypes } = require('../util/Constants'); const Permissions = require('../util/Permissions'); -const Util = require('../util/Util'); /** * Represents a guild channel from any of the following: @@ -262,27 +260,6 @@ class GuildChannel extends Channel { return this.guild.members.cache.filter(m => this.permissionsFor(m).has(Permissions.FLAGS.VIEW_CHANNEL, false)); } - /** - * The data for a guild channel. - * @typedef {Object} ChannelData - * @property {string} [name] The name of the channel - * @property {ChannelType} [type] The type of the channel (only conversion between text and news is supported) - * @property {number} [position] The position of the channel - * @property {string} [topic] The topic of the text channel - * @property {boolean} [nsfw] Whether the channel is NSFW - * @property {number} [bitrate] The bitrate of the voice channel - * @property {number} [userLimit] The user limit of the voice channel - * @property {?CategoryChannelResolvable} [parent] The parent of the channel - * @property {boolean} [lockPermissions] - * Lock the permissions of the channel to what the parent's permissions are - * @property {OverwriteResolvable[]|Collection} [permissionOverwrites] - * Permission overwrites for the channel - * @property {number} [rateLimitPerUser] The rate limit per user (slowmode) 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 - */ - /** * Edits the channel. * @param {ChannelData} data The new data for the channel @@ -294,64 +271,8 @@ class GuildChannel extends Channel { * .then(console.log) * .catch(console.error); */ - async edit(data, reason) { - data.parent &&= this.client.channels.resolveId(data.parent); - - if (typeof data.position !== 'undefined') { - const updatedChannels = await Util.setPosition( - this, - data.position, - false, - this.guild._sortedChannels(this), - this.client.api.guilds(this.guild.id).channels, - reason, - ); - this.client.actions.GuildChannelsPositionUpdate.handle({ - guild_id: this.guild.id, - channels: updatedChannels, - }); - } - - let permission_overwrites; - - if (data.permissionOverwrites) { - permission_overwrites = data.permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); - } - - if (data.lockPermissions) { - if (data.parent) { - const newParent = this.guild.channels.resolve(data.parent); - if (newParent?.type === 'GUILD_CATEGORY') { - permission_overwrites = newParent.permissionOverwrites.cache.map(o => - PermissionOverwrites.resolve(o, this.guild), - ); - } - } else if (this.parent) { - permission_overwrites = this.parent.permissionOverwrites.cache.map(o => - PermissionOverwrites.resolve(o, this.guild), - ); - } - } - - const newData = await this.client.api.channels(this.id).patch({ - data: { - name: (data.name ?? this.name).trim(), - type: ChannelTypes[data.type], - topic: data.topic, - nsfw: data.nsfw, - bitrate: data.bitrate ?? this.bitrate, - user_limit: data.userLimit ?? this.userLimit, - rtc_region: data.rtcRegion ?? this.rtcRegion, - parent_id: data.parent, - lock_permissions: data.lockPermissions, - rate_limit_per_user: data.rateLimitPerUser, - default_auto_archive_duration: data.defaultAutoArchiveDuration, - permission_overwrites, - }, - reason, - }); - - return this.client.actions.ChannelUpdate.handle(newData).updated; + edit(data, reason) { + return this.guild.channels.edit(this, data, reason); } /** @@ -415,30 +336,10 @@ class GuildChannel extends Channel { * .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`)) * .catch(console.error); */ - async setPosition(position, { relative, reason } = {}) { - const updatedChannels = await Util.setPosition( - this, - position, - relative, - this.guild._sortedChannels(this), - this.client.api.guilds(this.guild.id).channels, - reason, - ); - this.client.actions.GuildChannelsPositionUpdate.handle({ - guild_id: this.guild.id, - channels: updatedChannels, - }); - return this; + setPosition(position, options = {}) { + return this.guild.channels.setPosition(this, position, options); } - /** - * Data that can be resolved to an Application. This can be: - * * An Application - * * An Activity with associated Application - * * A Snowflake - * @typedef {Application|Snowflake} ApplicationResolvable - */ - /** * Options used to clone a guild channel. * @typedef {GuildChannelCreateOptions} GuildChannelCloneOptions @@ -544,7 +445,7 @@ class GuildChannel extends Channel { * .catch(console.error); */ async delete(reason) { - await this.client.api.channels(this.id).delete({ reason }); + await this.guild.channels.delete(this.id, reason); return this; } } diff --git a/src/structures/GuildEmoji.js b/src/structures/GuildEmoji.js index e5629fde91ee..abe263330994 100644 --- a/src/structures/GuildEmoji.js +++ b/src/structures/GuildEmoji.js @@ -72,18 +72,8 @@ class GuildEmoji extends BaseGuildEmoji { * Fetches the author for this emoji * @returns {Promise} */ - async fetchAuthor() { - if (this.managed) { - throw new Error('EMOJI_MANAGED'); - } else { - if (!this.guild.me) throw new Error('GUILD_UNCACHED_ME'); - if (!this.guild.me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS_AND_STICKERS)) { - throw new Error('MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION', this.guild); - } - } - const data = await this.client.api.guilds(this.guild.id).emojis(this.id).get(); - this._patch(data); - return this.author; + fetchAuthor() { + return this.guild.emojis.fetchAuthor(this); } /** @@ -137,7 +127,7 @@ class GuildEmoji extends BaseGuildEmoji { * @returns {Promise} */ async delete(reason) { - await this.client.api.guilds(this.guild.id).emojis(this.id).delete({ reason }); + await this.guild.emojis.delete(this, reason); return this; } diff --git a/src/structures/Role.js b/src/structures/Role.js index f0ee3a869792..fbacb749fb2e 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -5,7 +5,6 @@ const Base = require('./Base'); const { Error } = require('../errors'); const Permissions = require('../util/Permissions'); const SnowflakeUtil = require('../util/SnowflakeUtil'); -const Util = require('../util/Util'); let deprecationEmittedForComparePositions = false; @@ -399,20 +398,8 @@ class Role extends Base { * .then(updated => console.log(`Role position: ${updated.position}`)) * .catch(console.error); */ - async setPosition(position, { relative, reason } = {}) { - const updatedRoles = await Util.setPosition( - this, - position, - relative, - this.guild._sortedRoles(), - this.client.api.guilds(this.guild.id).roles, - reason, - ); - this.client.actions.GuildRolesPositionUpdate.handle({ - guild_id: this.guild.id, - roles: updatedRoles, - }); - return this; + setPosition(position, options = {}) { + return this.guild.roles.setPosition(this, position, options); } /** diff --git a/src/structures/Sticker.js b/src/structures/Sticker.js index a3347375957d..ed01632255c4 100644 --- a/src/structures/Sticker.js +++ b/src/structures/Sticker.js @@ -228,10 +228,7 @@ class Sticker extends Base { async fetchUser() { if (this.partial) await this.fetch(); if (!this.guildId) throw new Error('NOT_GUILD_STICKER'); - - const data = await this.client.api.guilds(this.guildId).stickers(this.id).get(); - this._patch(data); - return this.user; + return this.guild.stickers.fetchUser(this); } /** diff --git a/src/structures/ThreadChannel.js b/src/structures/ThreadChannel.js index 1f977285ee50..6f3e1a8ca516 100644 --- a/src/structures/ThreadChannel.js +++ b/src/structures/ThreadChannel.js @@ -535,7 +535,7 @@ class ThreadChannel extends Channel { * .catch(console.error); */ async delete(reason) { - await this.client.api.channels(this.id).delete({ reason }); + await this.guild.channels.delete(this.id, reason); return this; } diff --git a/typings/index.d.ts b/typings/index.d.ts index bf4f2edfd387..f9467733d254 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3029,12 +3029,24 @@ export class GuildChannelManager extends CachedManager; - public create(name: string, options?: GuildChannelCreateOptions): Promise; + public createWebhook( + channel: GuildChannelResolvable, + name: string, + options?: ChannelWebhookCreateOptions, + ): Promise; + public edit(channel: GuildChannelResolvable, data: ChannelData, reason?: string): Promise; public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; public fetch(id?: undefined, options?: BaseFetchOptions): Promise>; + public fetchWebhooks(channel: GuildChannelResolvable): Promise>; + public setPosition( + channel: GuildChannelResolvable, + position: number, + options?: SetChannelPositionOptions, + ): Promise; public setPositions(channelPositions: readonly ChannelPosition[]): Promise; public fetchActiveThreads(cache?: boolean): Promise; + public delete(channel: GuildChannelResolvable, reason?: string): Promise; } export class GuildEmojiManager extends BaseGuildEmojiManager { @@ -3047,6 +3059,9 @@ export class GuildEmojiManager extends BaseGuildEmojiManager { ): Promise; public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; public fetch(id?: undefined, options?: BaseFetchOptions): Promise>; + public fetchAuthor(emoji: EmojiResolvable): Promise; + public delete(emoji: EmojiResolvable, reason?: string): Promise; + public edit(emoji: EmojiResolvable, data: GuildEmojiEditData, reason?: string): Promise; } export class GuildEmojiRoleManager extends DataManager { @@ -3145,6 +3160,7 @@ export class GuildStickerManager extends CachedManager; public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; public fetch(id?: Snowflake, options?: BaseFetchOptions): Promise>; + public fetchUser(sticker: StickerResolvable): Promise; } export class GuildMemberRoleManager extends DataManager { @@ -3245,6 +3261,7 @@ export class RoleManager extends CachedManager public create(options?: CreateRoleOptions): Promise; public edit(role: RoleResolvable, options: RoleData, reason?: string): Promise; public delete(role: RoleResolvable, reason?: string): Promise; + public setPosition(role: RoleResolvable, position: number, options?: SetRolePositionOptions): Promise; public setPositions(rolePositions: readonly RolePosition[]): Promise; public comparePositions(role1: RoleResolvable, role2: RoleResolvable): number; }