Downgrade to v13

[vi] cảm giác đau khổ
This commit is contained in:
March 7th
2022-03-24 17:55:32 +07:00
parent 9596b1a210
commit 7dfdef46a5
218 changed files with 8584 additions and 9108 deletions

View File

@@ -1,11 +1,11 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const ApplicationCommand = require('../structures/ApplicationCommand');
const { ApplicationCommandTypes } = require('../util/Constants');
/**
* Manages API methods for application commands and stores their cache.
@@ -37,12 +37,12 @@ class ApplicationCommandManager extends CachedManager {
* @param {Snowflake} [options.id] The application command's id
* @param {Snowflake} [options.guildId] The guild's id to use in the path,
* ignored when using a {@link GuildApplicationCommandManager}
* @returns {string}
* @returns {Object}
* @private
*/
commandPath({ id, guildId } = {}) {
let path = this.client.api.applications(this.client.application.id);
if(this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId);
if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId);
return id ? path.commands(id) : path.commands;
}
@@ -169,7 +169,7 @@ class ApplicationCommandManager extends CachedManager {
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
const patched = await this.commandPath({ id, guildId }).patch({
body: this.constructor.transformCommand(data),
data: this.constructor.transformCommand(data),
});
return this._add(patched, true, guildId);
}
@@ -207,7 +207,7 @@ class ApplicationCommandManager extends CachedManager {
return {
name: command.name,
description: command.description,
type: command.type,
type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type],
options: command.options?.map(o => ApplicationCommand.transformOption(o)),
default_permission: command.defaultPermission ?? command.default_permission,
};

View File

@@ -1,9 +1,9 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { RESTJSONErrorCodes, Routes } = require('discord-api-types/v9');
const BaseManager = require('./BaseManager');
const { Error, TypeError } = require('../errors');
const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants');
/**
* Manages API methods for permissions of Application Commands.
@@ -43,7 +43,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* The APIRouter path to the commands
* @param {Snowflake} guildId The guild's id to use in the path,
* @param {Snowflake} [commandId] The application command's id
* @returns {string}
* @returns {Object}
* @private
*/
permissionsPath(guildId, commandId) {
@@ -137,7 +137,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* permissions: [
* {
* id: '876543210987654321',
* type: ApplicationCommandOptionType.User,
* type: 'USER',
* permission: false,
* },
* ]})
@@ -150,7 +150,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* id: '123456789012345678',
* permissions: [{
* id: '876543210987654321',
* type: ApplicationCommandOptionType.User,
* type: 'USER',
* permission: false,
* }],
* },
@@ -212,7 +212,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
* guild.commands.permissions.add({ command: '123456789012345678', permissions: [
* {
* id: '876543211234567890',
* type: ApplicationCommandPermissionType.Role,
* type: 'ROLE',
* permission: false
* },
* ]})
@@ -230,7 +230,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
try {
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error;
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
}
const newPermissions = permissions.slice();
@@ -319,7 +319,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
try {
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error;
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
}
const permissions = existing.filter(perm => !resolvedIds.includes(perm.id));
@@ -366,7 +366,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
try {
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error;
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
}
return existing.some(perm => perm.id === resolvedId);
@@ -388,6 +388,24 @@ class ApplicationCommandPermissionsManager extends BaseManager {
}
return { guildId, commandId };
}
/**
* Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API.
* @param {ApplicationCommandPermissionData} permissions The permissions to transform
* @param {boolean} [received] Whether these permissions have been received from Discord
* @returns {APIApplicationCommandPermissions}
* @private
*/
static transformPermissions(permissions, received) {
return {
id: permissions.id,
permission: permissions.permission,
type:
typeof permissions.type === 'number' && !received
? permissions.type
: ApplicationCommandPermissionTypes[permissions.type],
};
}
}
module.exports = ApplicationCommandPermissionsManager;

View File

@@ -1,6 +1,7 @@
'use strict';
const DataManager = require('./DataManager');
const { _cleanupSymbol } = require('../util/Constants');
/**
* Manages the API methods of a data model with a mutable cache of instances.
@@ -13,6 +14,19 @@ class CachedManager extends DataManager {
Object.defineProperty(this, '_cache', { value: this.client.options.makeCache(this.constructor, this.holds) });
let cleanup = this._cache[_cleanupSymbol]?.();
if (cleanup) {
cleanup = cleanup.bind(this._cache);
client._cleanups.add(cleanup);
client._finalizers.register(this, {
cleanup,
message:
`Garbage collection completed on ${this.constructor.name}, ` +
`which had a ${this._cache.constructor.name} of ${this.holds.name}.`,
name: this.constructor.name,
});
}
if (iterable) {
for (const item of iterable) {
this._add(item);

View File

@@ -1,69 +0,0 @@
'use strict';
const DataManager = require('./DataManager');
const GuildChannel = require('../structures/GuildChannel');
/**
* Manages API methods for CategoryChannels' children.
* @extends {DataManager}
*/
class CategoryChannelChildManager extends DataManager {
constructor(channel) {
super(channel.client, GuildChannel);
/**
* The category channel this manager belongs to
* @type {CategoryChannel}
*/
this.channel = channel;
}
/**
* The channels that are a part of this category
* @type {Collection<Snowflake, GuildChannel>}
* @readonly
*/
get cache() {
return this.guild.channels.cache.filter(c => c.parentId === this.channel.id);
}
/**
* The guild this manager belongs to
* @type {Guild}
* @readonly
*/
get guild() {
return this.channel.guild;
}
/**
* Options for creating a channel using {@link CategoryChannel#createChannel}.
* @typedef {Object} CategoryCreateChannelOptions
* @property {ChannelType} [type=ChannelType.GuildText] The type of the new channel.
* @property {string} [topic] The topic for the new channel
* @property {boolean} [nsfw] Whether the new channel is NSFW
* @property {number} [bitrate] Bitrate of the new channel in bits (only voice)
* @property {number} [userLimit] Maximum amount of users allowed in the new channel (only voice)
* @property {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} [permissionOverwrites]
* Permission overwrites of the new channel
* @property {number} [position] Position of the new channel
* @property {number} [rateLimitPerUser] The rate limit per user (slowmode) for the new channel in seconds
* @property {string} [rtcRegion] The specific region of the new channel.
* @property {string} [reason] Reason for creating the new channel
*/
/**
* Creates a new channel within this category.
* <info>You cannot create a channel of type {@link ChannelType.GuildCategory} inside a CategoryChannel.</info>
* @param {string} name The name of the new channel
* @param {CategoryCreateChannelOptions} options Options for creating the new channel
* @returns {Promise<GuildChannel>}
*/
create(name, options) {
return this.guild.channels.create(name, {
...options,
parent: this.channel.id,
});
}
}
module.exports = CategoryChannelChildManager;

View File

@@ -1,11 +1,9 @@
'use strict';
const process = require('node:process');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { Channel } = require('../structures/Channel');
const { ThreadChannelTypes } = require('../util/Constants');
const Events = require('../util/Events');
const { Events, ThreadChannelTypes } = require('../util/Constants');
let cacheWarningEmitted = false;
@@ -18,8 +16,8 @@ class ChannelManager extends CachedManager {
super(client, Channel, iterable);
const defaultCaching =
this._cache.constructor.name === 'Collection' ||
this._cache.maxSize === undefined ||
this._cache.maxSize === Infinity;
((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) &&
(this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault));
if (!cacheWarningEmitted && !defaultCaching) {
cacheWarningEmitted = true;
process.emitWarning(
@@ -49,7 +47,7 @@ class ChannelManager extends CachedManager {
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}`);
this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`);
return null;
}

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors');
const GuildBan = require('../structures/GuildBan');
@@ -120,7 +119,7 @@ class GuildBanManager extends CachedManager {
/**
* Options used to ban a user from a guild.
* @typedef {Object} BanOptions
* @property {number} [deleteMessageDays] Number of days of messages to delete, must be between 0 and 7, inclusive
* @property {number} [days=0] Number of days of messages to delete, must be between 0 and 7, inclusive
* @property {string} [reason] The reason for the ban
*/
@@ -137,14 +136,17 @@ class GuildBanManager extends CachedManager {
* .then(banInfo => console.log(`Banned ${banInfo.user?.tag ?? banInfo.tag ?? banInfo}`))
* .catch(console.error);
*/
async create(user, options = {}) {
async create(user, options = { days: 0 }) {
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
const id = this.client.users.resolveId(user);
if (!id) throw new Error('BAN_RESOLVE_ID', true);
await this.client.api.guilds(this.guild.id).bans(id).put({
body: { delete_message_days: options.deleteMessageDays },
reason: options.reason,
});
await this.client.api
.guilds(this.guild.id)
.bans(id)
.put({
data: { delete_message_days: options.days },
reason: options.reason,
});
if (user instanceof GuildMember) return user;
const _user = this.client.users.resolve(id);
if (_user) {

View File

@@ -2,17 +2,13 @@
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { ChannelType, Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const ThreadManager = require('./ThreadManager');
const { Error, TypeError } = require('../errors');
const { Error } = require('../errors');
const GuildChannel = require('../structures/GuildChannel');
const PermissionOverwrites = require('../structures/PermissionOverwrites');
const ThreadChannel = require('../structures/ThreadChannel');
const Webhook = require('../structures/Webhook');
const { ThreadChannelTypes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Util = require('../util/Util');
const { ChannelTypes, ThreadChannelTypes } = require('../util/Constants');
let cacheWarningEmitted = false;
let storeChannelDeprecationEmitted = false;
@@ -26,8 +22,8 @@ class GuildChannelManager extends CachedManager {
super(guild.client, GuildChannel, iterable);
const defaultCaching =
this._cache.constructor.name === 'Collection' ||
this._cache.maxSize === undefined ||
this._cache.maxSize === Infinity;
((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) &&
(this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault));
if (!cacheWarningEmitted && !defaultCaching) {
cacheWarningEmitted = true;
process.emitWarning(
@@ -116,11 +112,11 @@ class GuildChannelManager extends CachedManager {
* @example
* // Create a new channel with permission overwrites
* guild.channels.create('new-voice', {
* type: ChannelType.GuildVoice,
* type: 'GUILD_VOICE',
* permissionOverwrites: [
* {
* id: message.author.id,
* deny: [PermissionFlagsBits.ViewChannel],
* deny: [Permissions.FLAGS.VIEW_CHANNEL],
* },
* ],
* })
@@ -143,8 +139,9 @@ class GuildChannelManager extends CachedManager {
) {
parent &&= this.client.channels.resolveId(parent);
permissionOverwrites &&= permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
const intType = typeof type === 'number' ? type : ChannelTypes[type] ?? ChannelTypes.GUILD_TEXT;
if (type === ChannelType.GuildStore && !storeChannelDeprecationEmitted) {
if (intType === ChannelTypes.GUILD_STORE && !storeChannelDeprecationEmitted) {
storeChannelDeprecationEmitted = true;
process.emitWarning(
// eslint-disable-next-line max-len
@@ -154,10 +151,10 @@ class GuildChannelManager extends CachedManager {
}
const data = await this.client.api.guilds(this.guild.id).channels.post({
body: {
data: {
name,
topic,
type,
type: intType,
nsfw,
bitrate,
user_limit: userLimit,
@@ -172,148 +169,6 @@ 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<Webhook>} 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({
body: {
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<Snowflake, OverwriteResolvable>} [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<GuildChannel>}
* @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 === ChannelType.GuildCategory) {
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({
body: {
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<GuildChannel>}
* @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(channel),
this.client,
Routes.guildChannels(this.guild.id),
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
@@ -349,39 +204,6 @@ class GuildChannelManager extends CachedManager {
return channels;
}
/**
* Fetches all webhooks for the channel.
* @param {GuildChannelResolvable} channel The channel to fetch webhooks for
* @returns {Promise<Collection<Snowflake, Webhook>>}
* @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.
* <info>Only one channel's parent can be changed at a time</info>
@@ -400,7 +222,7 @@ class GuildChannelManager extends CachedManager {
parent_id: typeof r.parent !== 'undefined' ? this.channels.resolveId(r.parent) : undefined,
}));
await this.client.api.guilds(this.guild.id).channels.post({ body: channelPositions });
await this.client.api.guilds(this.guild.id).channels.patch({ data: channelPositions });
return this.client.actions.GuildChannelsPositionUpdate.handle({
guild_id: this.guild.id,
channels: channelPositions,
@@ -421,23 +243,6 @@ 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<void>}
* @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(id).delete({ reason });
}
}
module.exports = GuildChannelManager;

View File

@@ -1,9 +1,8 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes, PermissionFlagsBits } = require('discord-api-types/v9');
const BaseGuildEmojiManager = require('./BaseGuildEmojiManager');
const { Error, TypeError } = require('../errors');
const { TypeError } = require('../errors');
const DataResolver = require('../util/DataResolver');
/**
@@ -53,20 +52,20 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
attachment = await DataResolver.resolveImage(attachment);
if (!attachment) throw new TypeError('REQ_RESOURCE_TYPE');
const body = { image: attachment, name };
const data = { image: attachment, name };
if (roles) {
if (!Array.isArray(roles) && !(roles instanceof Collection)) {
throw new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true);
}
body.roles = [];
data.roles = [];
for (const role of roles.values()) {
const resolvedRole = this.guild.roles.resolveId(role);
if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'options.roles', role);
body.roles.push(resolvedRole);
data.roles.push(resolvedRole);
}
}
const emoji = await this.client.api.guilds(this.guild.id).emojis.post({ body, reason });
const emoji = await this.client.api.guilds(this.guild.id).emojis.post({ data, reason });
return this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji;
}
@@ -101,68 +100,6 @@ 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<void>}
*/
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<GuildEmoji>}
*/
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({
body: {
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<User>}
*/
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(PermissionFlagsBits.ManageEmojisAndStickers)) {
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;

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { Error } = require('../errors');
const Invite = require('../structures/Invite');
@@ -178,13 +177,13 @@ class GuildInviteManager extends CachedManager {
*/
async create(
channel,
{ temporary, maxAge, maxUses, unique, targetUser, targetApplication, targetType, reason } = {},
{ temporary = false, maxAge = 86400, maxUses = 0, unique, targetUser, targetApplication, targetType, reason } = {},
) {
const id = this.guild.channels.resolveId(channel);
if (!id) throw new Error('GUILD_CHANNEL_RESOLVE');
const invite = await this.client.api.channels(id).invites.post({
body: {
data: {
temporary,
max_age: maxAge,
max_uses: maxUses,

View File

@@ -1,9 +1,8 @@
'use strict';
const process = require('node:process');
const { setTimeout, clearTimeout } = require('node:timers');
const { setTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { Guild } = require('../structures/Guild');
const GuildChannel = require('../structures/GuildChannel');
@@ -12,10 +11,17 @@ const { GuildMember } = require('../structures/GuildMember');
const Invite = require('../structures/Invite');
const OAuth2Guild = require('../structures/OAuth2Guild');
const { Role } = require('../structures/Role');
const {
ChannelTypes,
Events,
OverwriteTypes,
VerificationLevels,
DefaultMessageNotificationLevels,
ExplicitContentFilterLevels,
} = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Events = require('../util/Events');
const PermissionsBitField = require('../util/PermissionsBitField');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const Permissions = require('../util/Permissions');
const SystemChannelFlags = require('../util/SystemChannelFlags');
const { resolveColor } = require('../util/Util');
let cacheWarningEmitted = false;
@@ -175,8 +181,17 @@ class GuildManager extends CachedManager {
} = {},
) {
icon = await DataResolver.resolveImage(icon);
if (typeof verificationLevel === 'string') {
verificationLevel = VerificationLevels[verificationLevel];
}
if (typeof defaultMessageNotifications === 'string') {
defaultMessageNotifications = DefaultMessageNotificationLevels[defaultMessageNotifications];
}
if (typeof explicitContentFilter === 'string') {
explicitContentFilter = ExplicitContentFilterLevels[explicitContentFilter];
}
for (const channel of channels) {
channel.type &&= typeof channel.type === 'number' ? channel.type : ChannelTypes[channel.type];
channel.parent_id = channel.parentId;
delete channel.parentId;
channel.user_limit = channel.userLimit;
@@ -188,20 +203,23 @@ class GuildManager extends CachedManager {
if (!channel.permissionOverwrites) continue;
for (const overwrite of channel.permissionOverwrites) {
overwrite.allow &&= PermissionsBitField.resolve(overwrite.allow).toString();
overwrite.deny &&= PermissionsBitField.resolve(overwrite.deny).toString();
if (typeof overwrite.type === 'string') {
overwrite.type = OverwriteTypes[overwrite.type];
}
overwrite.allow &&= Permissions.resolve(overwrite.allow).toString();
overwrite.deny &&= Permissions.resolve(overwrite.deny).toString();
}
channel.permission_overwrites = channel.permissionOverwrites;
delete channel.permissionOverwrites;
}
for (const role of roles) {
role.color &&= resolveColor(role.color);
role.permissions &&= PermissionsBitField.resolve(role.permissions).toString();
role.permissions &&= Permissions.resolve(role.permissions).toString();
}
systemChannelFlags &&= SystemChannelFlagsBitField.resolve(systemChannelFlags);
systemChannelFlags &&= SystemChannelFlags.resolve(systemChannelFlags);
const data = await this.client.api.guilds.post({
body: {
data: {
name,
icon,
verification_level: verificationLevel,
@@ -222,16 +240,16 @@ class GuildManager extends CachedManager {
const handleGuild = guild => {
if (guild.id === data.id) {
clearTimeout(timeout);
this.client.removeListener(Events.GuildCreate, handleGuild);
this.client.removeListener(Events.GUILD_CREATE, handleGuild);
this.client.decrementMaxListeners();
resolve(guild);
}
};
this.client.incrementMaxListeners();
this.client.on(Events.GuildCreate, handleGuild);
this.client.on(Events.GUILD_CREATE, handleGuild);
const timeout = setTimeout(() => {
this.client.removeListener(Events.GuildCreate, handleGuild);
this.client.removeListener(Events.GUILD_CREATE, handleGuild);
this.client.decrementMaxListeners();
resolve(this.client.guilds._add(data));
}, 10_000).unref();
@@ -250,7 +268,7 @@ class GuildManager extends CachedManager {
* @typedef {Object} FetchGuildsOptions
* @property {Snowflake} [before] Get guilds before this guild id
* @property {Snowflake} [after] Get guilds after this guild id
* @property {number} [limit] Maximum number of guilds to request (1-200)
* @property {number} [limit=200] Maximum number of guilds to request (1-200)
*/
/**
@@ -267,13 +285,11 @@ class GuildManager extends CachedManager {
if (existing) return existing;
}
const data = await this.client.api.guilds(id).get({
query: new URLSearchParams({ with_counts: options.withCounts ?? true }),
})
const data = await this.client.api.guilds(id).get({ query: { with_counts: options.withCounts ?? true } });
return this._add(data, options.cache);
}
const data = await this.client.api.users('@me').guilds.get({ query: new URLSearchParams(options) });
const data = await this.client.api.users('@me').guilds.get({ query: options });
return data.reduce((coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)), new Collection());
}
}

View File

@@ -1,16 +1,15 @@
'use strict';
const { Buffer } = require('node:buffer');
const { setTimeout, clearTimeout } = require('node:timers');
const { setTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const { Routes, GatewayOpcodes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { Error, TypeError, RangeError } = require('../errors');
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
const { GuildMember } = require('../structures/GuildMember');
const { Role } = require('../structures/Role');
const Events = require('../util/Events');
const { Events, Opcodes } = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil');
/**
* Manages API methods for GuildMembers and stores their cache.
@@ -114,7 +113,7 @@ class GuildMemberManager extends CachedManager {
}
resolvedOptions.roles = resolvedRoles;
}
const data = await this.client.rest.put(Routes.guildMember(this.guild.id, userId), { body: resolvedOptions });
const data = await this.client.api.guilds(this.guild.id).members(userId).put({ data: resolvedOptions });
// Data is an empty buffer if the member is already part of the guild.
return data instanceof Buffer ? (options.fetchWhenExisting === false ? null : this.fetch(userId)) : this._add(data);
}
@@ -194,7 +193,7 @@ class GuildMemberManager extends CachedManager {
* Options used for searching guild members.
* @typedef {Object} GuildSearchMembersOptions
* @property {string} query Filter members whose username or nickname start with this query
* @property {number} [limit] Maximum number of members to search
* @property {number} [limit=1] Maximum number of members to search
* @property {boolean} [cache=true] Whether or not to cache the fetched member(s)
*/
@@ -203,10 +202,8 @@ class GuildMemberManager extends CachedManager {
* @param {GuildSearchMembersOptions} options Options for searching members
* @returns {Promise<Collection<Snowflake, GuildMember>>}
*/
async search({ query, limit, cache = true } = {}) {
const data = await this.client.api.guilds(this.guild.id).members.search.get({
query: new URLSearchParams({ query, limit }),
})
async search({ query, limit = 1, cache = true } = {}) {
const data = await this.client.api.guilds(this.guild.id).members.search.get({ query: { query, limit } });
return data.reduce((col, member) => col.set(member.user.id, this._add(member, cache)), new Collection());
}
@@ -214,7 +211,7 @@ class GuildMemberManager extends CachedManager {
* Options used for listing guild members.
* @typedef {Object} GuildListMembersOptions
* @property {Snowflake} [after] Limit fetching members to those with an id greater than the supplied id
* @property {number} [limit] Maximum number of members to list
* @property {number} [limit=1] Maximum number of members to list
* @property {boolean} [cache=true] Whether or not to cache the fetched member(s)
*/
@@ -223,12 +220,8 @@ class GuildMemberManager extends CachedManager {
* @param {GuildListMembersOptions} [options] Options for listing members
* @returns {Promise<Collection<Snowflake, GuildMember>>}
*/
async list({ after, limit, cache = true } = {}) {
const query = new URLSearchParams({ limit });
if (after) {
query.set('after', after);
}
const data = await this.client.api.guilds(this.guild.id).members.get({ query });
async list({ after, limit = 1, cache = true } = {}) {
const data = await this.client.api.guilds(this.guild.id).members.get({ query: { after, limit } });
return data.reduce((col, member) => col.set(member.user.id, this._add(member, cache)), new Collection());
}
@@ -273,10 +266,7 @@ class GuildMemberManager extends CachedManager {
_data.roles &&= _data.roles.map(role => (role instanceof Role ? role.id : role));
_data.communication_disabled_until =
// eslint-disable-next-line eqeqeq
_data.communicationDisabledUntil != null
? new Date(_data.communicationDisabledUntil).toISOString()
: _data.communicationDisabledUntil;
_data.communicationDisabledUntil && new Date(_data.communicationDisabledUntil).toISOString();
let endpoint = this.client.api.guilds(this.guild.id);
if (id === this.client.user.id) {
@@ -298,9 +288,9 @@ class GuildMemberManager extends CachedManager {
* <info>It's recommended to set {@link GuildPruneMembersOptions#count options.count}
* to `false` for large guilds.</info>
* @typedef {Object} GuildPruneMembersOptions
* @property {number} [days] Number of days of inactivity required to kick
* @property {number} [days=7] Number of days of inactivity required to kick
* @property {boolean} [dry=false] Get the number of users that will be kicked, without actually kicking them
* @property {boolean} [count] Whether or not to return the number of users that have been kicked.
* @property {boolean} [count=true] Whether or not to return the number of users that have been kicked.
* @property {RoleResolvable[]} [roles] Array of roles to bypass the "...and no roles" constraint when pruning
* @property {string} [reason] Reason for this prune
*/
@@ -325,7 +315,7 @@ class GuildMemberManager extends CachedManager {
* .then(pruned => console.log(`I just pruned ${pruned} people!`))
* .catch(console.error);
*/
async prune({ days, dry = false, count: compute_prune_count, roles = [], reason } = {}) {
async prune({ days = 7, dry = false, count: compute_prune_count = true, roles = [], reason } = {}) {
if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE');
const query = { days };
@@ -346,8 +336,8 @@ class GuildMemberManager extends CachedManager {
const endpoint = this.client.api.guilds(this.guild.id).prune;
const { pruned } = await (dry
? endpoint.get({ query: new URLSearchParams(query), reason })
: endpoint.post({ body: { ...query, compute_prune_count }, reason }));
? endpoint.get({ query, reason })
: endpoint.post({ data: { ...query, compute_prune_count }, reason }));
return pruned;
}
@@ -363,14 +353,14 @@ class GuildMemberManager extends CachedManager {
* @example
* // Kick a user by id (or with a user/guild member object)
* guild.members.kick('84484653687267328')
* .then(kickInfo => console.log(`Kicked ${kickInfo.user?.tag ?? kickInfo.tag ?? kickInfo}`))
* .then(banInfo => console.log(`Kicked ${banInfo.user?.tag ?? banInfo.tag ?? banInfo}`))
* .catch(console.error);
*/
async kick(user, reason) {
const id = this.client.users.resolveId(user);
if (!id) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable'));
await this.clinet.api.guilds(this.guild.id).members(id).delete({ reason });
await this.client.api.guilds(this.guild.id).members(id).delete({ reason });
return this.resolve(user) ?? this.client.users.resolve(user) ?? id;
}
@@ -386,10 +376,10 @@ class GuildMemberManager extends CachedManager {
* @example
* // Ban a user by id (or with a user/guild member object)
* guild.members.ban('84484653687267328')
* .then(banInfo => console.log(`Banned ${banInfo.user?.tag ?? banInfo.tag ?? banInfo}`))
* .then(kickInfo => console.log(`Banned ${kickInfo.user?.tag ?? kickInfo.tag ?? kickInfo}`))
* .catch(console.error);
*/
ban(user, options) {
ban(user, options = { days: 0 }) {
return this.guild.bans.create(user, options);
}
@@ -424,13 +414,13 @@ class GuildMemberManager extends CachedManager {
user: user_ids,
query,
time = 120e3,
nonce = DiscordSnowflake.generate().toString(),
nonce = SnowflakeUtil.generate(),
} = {}) {
return new Promise((resolve, reject) => {
if (!query && !user_ids) query = '';
if (nonce.length > 32) throw new RangeError('MEMBER_FETCH_NONCE_LENGTH');
this.guild.shard.send({
op: GatewayOpcodes.RequestGuildMembers,
op: Opcodes.REQUEST_GUILD_MEMBERS,
d: {
guild_id: this.guild.id,
presences,
@@ -451,7 +441,7 @@ class GuildMemberManager extends CachedManager {
}
if (members.size < 1_000 || (limit && fetchedMembers.size >= limit) || i === chunk.count) {
clearTimeout(timeout);
this.client.removeListener(Events.GuildMembersChunk, handler);
this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler);
this.client.decrementMaxListeners();
let fetched = fetchedMembers;
if (user_ids && !Array.isArray(user_ids) && fetched.size) fetched = fetched.first();
@@ -459,12 +449,12 @@ class GuildMemberManager extends CachedManager {
}
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.GuildMembersChunk, handler);
this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler);
this.client.decrementMaxListeners();
reject(new Error('GUILD_MEMBERS_TIMEOUT'));
}, time).unref();
this.client.incrementMaxListeners();
this.client.on(Events.GuildMembersChunk, handler);
this.client.on(Events.GUILD_MEMBERS_CHUNK, handler);
});
}
}

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const DataManager = require('./DataManager');
const { TypeError } = require('../errors');
const { Role } = require('../structures/Role');

View File

@@ -1,11 +1,10 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { GuildScheduledEventEntityType, Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors');
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
const DataResolver = require('../util/DataResolver');
const { PrivacyLevels, GuildScheduledEventEntityTypes, GuildScheduledEventStatuses } = require('../util/Constants');
/**
* Manages API methods for GuildScheduledEvents and stores their cache.
@@ -41,17 +40,15 @@ class GuildScheduledEventManager extends CachedManager {
* @property {string} name The name of the guild scheduled event
* @property {DateResolvable} scheduledStartTime The time to schedule the event at
* @property {DateResolvable} [scheduledEndTime] The time to end the event at
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.External}</warn>
* <warn>This is required if `entityType` is 'EXTERNAL'</warn>
* @property {PrivacyLevel|number} privacyLevel The privacy level of the guild scheduled event
* @property {GuildScheduledEventEntityType|number} entityType The scheduled entity type of the event
* @property {string} [description] The description of the guild scheduled event
* @property {GuildVoiceChannelResolvable} [channel] The channel of the guild scheduled event
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.StageInstance} or
* {@link GuildScheduledEventEntityType.Voice}</warn>
* <warn>This is required if `entityType` is 'STAGE_INSTANCE' or `VOICE`</warn>
* @property {GuildScheduledEventEntityMetadataOptions} [entityMetadata] The entity metadata of the
* guild scheduled event
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* <warn>This is required if `entityType` is 'EXTERNAL'</warn>
* @property {string} [reason] The reason for creating the guild scheduled event
*/
@@ -59,7 +56,7 @@ class GuildScheduledEventManager extends CachedManager {
* Options used to set entity metadata of a guild scheduled event.
* @typedef {Object} GuildScheduledEventEntityMetadataOptions
* @property {string} [location] The location of the guild scheduled event
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.External}</warn>
* <warn>This is required if `entityType` is 'EXTERNAL'</warn>
*/
/**
@@ -79,11 +76,13 @@ class GuildScheduledEventManager extends CachedManager {
scheduledEndTime,
entityMetadata,
reason,
image,
} = options;
if (typeof privacyLevel === 'string') privacyLevel = PrivacyLevels[privacyLevel];
if (typeof entityType === 'string') entityType = GuildScheduledEventEntityTypes[entityType];
let entity_metadata, channel_id;
if (entityType === GuildScheduledEventEntityType.External) {
if (entityType === GuildScheduledEventEntityTypes.EXTERNAL) {
channel_id = typeof channel === 'undefined' ? channel : null;
entity_metadata = { location: entityMetadata?.location };
} else {
@@ -93,7 +92,7 @@ class GuildScheduledEventManager extends CachedManager {
}
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events').post({
body: {
data: {
channel_id,
name,
privacy_level: privacyLevel,
@@ -102,10 +101,9 @@ class GuildScheduledEventManager extends CachedManager {
description,
entity_type: entityType,
entity_metadata,
image: image && (await DataResolver.resolveImage(image)),
},
reason,
})
});
return this._add(data);
}
@@ -140,15 +138,15 @@ class GuildScheduledEventManager extends CachedManager {
if (existing) return existing;
}
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', id).get({
query: new URLSearchParams({ with_user_count: options.withUserCount ?? true }),
})
const data = await this.client.api
.guilds(this.guild.id, 'scheduled-events', id)
.get({ query: { with_user_count: options.withUserCount ?? true } });
return this._add(data, options.cache);
}
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events').get({
query: new URLSearchParams({ with_user_count: options.withUserCount ?? true }),
})
const data = await this.client.api
.guilds(this.guild.id, 'scheduled-events')
.get({ query: { with_user_count: options.withUserCount ?? true } });
return data.reduce(
(coll, rawGuildScheduledEventData) =>
@@ -173,9 +171,7 @@ class GuildScheduledEventManager extends CachedManager {
* @property {GuildScheduledEventStatus|number} [status] The status of the guild scheduled event
* @property {GuildScheduledEventEntityMetadataOptions} [entityMetadata] The entity metadata of the
* guild scheduled event
* <warn>This can be modified only if `entityType` of the `GuildScheduledEvent` to be edited is
* {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* <warn>This can be modified only if `entityType` of the `GuildScheduledEvent` to be edited is 'EXTERNAL'</warn>
* @property {string} [reason] The reason for editing the guild scheduled event
*/
@@ -201,9 +197,12 @@ class GuildScheduledEventManager extends CachedManager {
scheduledEndTime,
entityMetadata,
reason,
image,
} = options;
if (typeof privacyLevel === 'string') privacyLevel = PrivacyLevels[privacyLevel];
if (typeof entityType === 'string') entityType = GuildScheduledEventEntityTypes[entityType];
if (typeof status === 'string') status = GuildScheduledEventStatuses[status];
let entity_metadata;
if (entityMetadata) {
entity_metadata = {
@@ -212,7 +211,7 @@ class GuildScheduledEventManager extends CachedManager {
}
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).patch({
body: {
data: {
channel_id: typeof channel === 'undefined' ? channel : this.guild.channels.resolveId(channel),
name,
privacy_level: privacyLevel,
@@ -221,11 +220,10 @@ class GuildScheduledEventManager extends CachedManager {
description,
entity_type: entityType,
status,
image: image && (await DataResolver.resolveImage(image)),
entity_metadata,
},
reason,
})
});
return this._add(data);
}
@@ -272,26 +270,8 @@ class GuildScheduledEventManager extends CachedManager {
let { limit, withMember, before, after } = options;
const query = new URLSearchParams();
if (limit) {
query.set('limit', limit);
}
if (typeof withMember !== 'undefined') {
query.set('with_member', withMember);
}
if (before) {
query.set('before', before);
}
if (after) {
query.set('after', after);
}
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).users.get({
query,
query: { limit, with_member: withMember, before, after },
});
return data.reduce(

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const MessagePayload = require('../structures/MessagePayload');
@@ -62,14 +61,11 @@ class GuildStickerManager extends CachedManager {
if (!resolvedFile) throw new TypeError('REQ_RESOURCE_TYPE');
file = { ...resolvedFile, key: 'file' };
const body = { name, tags, description: description ?? '' };
const data = { name, tags, description: description ?? '' };
const sticker = await this.client.api.guilds(this.guild.id).stickers.post({
appendToFormData: true,
body,
files: [file],
reason,
});
const sticker = await this.client.api
.guilds(this.guild.id)
.stickers.post({ data, files: [file], reason, dontUsePayloadJSON: true });
return this.client.actions.GuildStickerCreate.handle(this.guild, sticker).sticker;
}
@@ -110,7 +106,7 @@ class GuildStickerManager extends CachedManager {
if (!stickerId) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
const d = await this.client.api.guilds(this.guild.id).stickers(stickerId).patch({
body: data,
data,
reason,
});
@@ -165,19 +161,6 @@ 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<?User>}
*/
async fetchUser(sticker) {
sticker = this.resolve(sticker);
if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
const data = await this.client.api.guilds(this.guild.id).stickers(sticker.id).get();
sticker._patch(data);
return sticker.user;
}
}
module.exports = GuildStickerManager;

View File

@@ -1,13 +1,11 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const { Message } = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload');
const Util = require('../util/Util');
const DiscordAPIError = require('../rest/DiscordAPIError');
/**
* Manages API methods for Messages and holds their cache.
@@ -38,7 +36,7 @@ class MessageManager extends CachedManager {
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
* `after` are mutually exclusive. All the parameters are optional.
* @typedef {Object} ChannelLogsQueryOptions
* @property {number} [limit] Number of messages to acquire
* @property {number} [limit=50] Number of messages to acquire
* @property {Snowflake} [before] The message's id to get the messages that were posted before it
* @property {Snowflake} [after] The message's id to get the messages that were posted after it
* @property {Snowflake} [around] The message's id to get the messages that were posted around it
@@ -84,7 +82,7 @@ class MessageManager extends CachedManager {
* .catch(console.error);
*/
async fetchPinned(cache = true) {
const data = await this.client.api.channels(this.channel.id).pins.get();
const data = await this.client.api.channels[this.channel.id].pins.get();
const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache));
return messages;
@@ -125,13 +123,14 @@ class MessageManager extends CachedManager {
const messageId = this.resolveId(message);
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
const { body, files } = await (options instanceof MessagePayload
const { data, files } = await (options instanceof MessagePayload
? options
: MessagePayload.create(message instanceof Message ? message : this, options)
)
.resolveBody()
.resolveData()
.resolveFiles();
const d = await this.client.api.channels(this.channel.id).messages(messageId).patch({ body, files });
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
const existing = this.cache.get(messageId);
if (existing) {
const clone = existing._clone();
@@ -157,27 +156,25 @@ class MessageManager extends CachedManager {
/**
* Pins a message to the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to pin
* @param {string} [reason] Reason for pinning
* @returns {Promise<void>}
*/
async pin(message, reason) {
async pin(message) {
message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
await this.client.api.channels(this.channel.id).pins(message).put({ reason });
await this.client.api.channels(this.channel.id).pins(message).put();
}
/**
* Unpins a message from the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to unpin
* @param {string} [reason] Reason for unpinning
* @returns {Promise<void>}
*/
async unpin(message, reason) {
async unpin(message) {
message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
await this.client.api.channels(this.channel.id).pins(message).delete({ reason });
await this.client.api.channels(this.channel.id).pins(message).delete();
}
/**
@@ -197,6 +194,7 @@ class MessageManager extends CachedManager {
? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}`
: encodeURIComponent(emoji.name);
// eslint-disable-next-line newline-per-chained-call
await this.client.api.channels(this.channel.id).messages(message).reactions(emojiId, '@me').put();
}
@@ -218,14 +216,12 @@ class MessageManager extends CachedManager {
if (existing && !existing.partial) return existing;
}
const data = await this.client.api.channels(this.channel.id).messages(messageId).get();
const data = await this.client.api.channels[this.channel.id].messages[messageId].get();
return this._add(data, cache);
}
async _fetchMany(options = {}, cache) {
const data = await this.client.api.channels(this.channel.id).messages.get({
query: new URLSearchParams(options),
});
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache));
return messages;

View File

@@ -2,11 +2,11 @@
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { OverwriteType, Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const PermissionOverwrites = require('../structures/PermissionOverwrites');
const { Role } = require('../structures/Role');
const { OverwriteTypes } = require('../util/Constants');
let cacheWarningEmitted = false;
@@ -58,7 +58,7 @@ class PermissionOverwriteManager extends CachedManager {
* message.channel.permissionOverwrites.set([
* {
* id: message.author.id,
* deny: [PermissionsFlagsBit.ViewChannel],
* deny: [Permissions.FLAGS.VIEW_CHANNEL],
* },
* ], 'Needed to change permissions');
*/
@@ -94,15 +94,18 @@ class PermissionOverwriteManager extends CachedManager {
if (typeof type !== 'number') {
userOrRole = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole);
if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
type = userOrRole instanceof Role ? OverwriteType.Role : OverwriteType.Member;
type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member;
}
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options, existing);
await this.client.api.channels(this.channel.id).permissions(userOrRoleId).put({
body: { id: userOrRoleId, type, allow, deny },
reason,
});
await this.client.api
.channels(this.channel.id)
.permissions(userOrRoleId)
.put({
data: { id: userOrRoleId, type, allow, deny },
reason,
});
return this.channel;
}

View File

@@ -1,6 +1,5 @@
'use strict';
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const MessageReaction = require('../structures/MessageReaction');

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { Error } = require('../errors');
const User = require('../structures/User');
@@ -41,11 +40,9 @@ class ReactionUserManager extends CachedManager {
*/
async fetch({ limit = 100, after } = {}) {
const message = this.reaction.message;
const query = new URLSearchParams({ limit });
if (after) {
query.set('after', after);
}
const data = await this.client.api.channels(message.channelId).messages(message.id).reactions(this.reaction.emoji.identifier).get({ query });
const data = await this.client.api.channels[message.channelId].messages[message.id].reactions[
this.reaction.emoji.identifier
].get({ query: { limit, after } });
const users = new Collection();
for (const rawUser of data) {
const user = this.client.users._add(rawUser);
@@ -64,10 +61,9 @@ class ReactionUserManager extends CachedManager {
const userId = this.client.users.resolveId(user);
if (!userId) throw new Error('REACTION_RESOLVE_USER');
const message = this.reaction.message;
await this.client.api.channels[message.channelId]
.messages[message.id]
.reactions[this.reaction.emoji.identifier][userId === this.client.user.id ? '@me' : userId]
.delete();
await this.client.api.channels[message.channelId].messages[message.id].reactions[this.reaction.emoji.identifier][
userId === this.client.user.id ? '@me' : userId
].delete();
return this.reaction;
}
}

View File

@@ -2,14 +2,12 @@
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const { Role } = require('../structures/Role');
const DataResolver = require('../util/DataResolver');
const PermissionsBitField = require('../util/PermissionsBitField');
const { resolveColor } = require('../util/Util');
const Util = require('../util/Util');
const Permissions = require('../util/Permissions');
const { resolveColor, setPosition } = require('../util/Util');
let cacheWarningEmitted = false;
@@ -129,7 +127,7 @@ class RoleManager extends CachedManager {
* // Create a new role with data and a reason
* guild.roles.create({
* name: 'Super Cool Blue People',
* color: Colors.Blue,
* color: 'BLUE',
* reason: 'we needed a role for Super Cool People',
* })
* .then(console.log)
@@ -138,7 +136,7 @@ class RoleManager extends CachedManager {
async create(options = {}) {
let { name, color, hoist, permissions, position, mentionable, reason, icon, unicodeEmoji } = options;
color &&= resolveColor(color);
if (typeof permissions !== 'undefined') permissions = new PermissionsBitField(permissions);
if (typeof permissions !== 'undefined') permissions = new Permissions(permissions);
if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.url;
icon = guildEmojiURL ? await DataResolver.resolveImage(guildEmojiURL) : await DataResolver.resolveImage(icon);
@@ -146,7 +144,7 @@ class RoleManager extends CachedManager {
}
const data = await this.client.api.guilds(this.guild.id).roles.post({
body: {
data: {
name,
color,
hoist,
@@ -156,12 +154,12 @@ class RoleManager extends CachedManager {
unicode_emoji: unicodeEmoji,
},
reason,
})
});
const { role } = this.client.actions.GuildRoleCreate.handle({
guild_id: this.guild.id,
role: data,
});
if (position) return this.setPosition(role, position, { reason });
if (position) return role.setPosition(position, reason);
return role;
}
@@ -182,7 +180,19 @@ class RoleManager extends CachedManager {
if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
if (typeof data.position === 'number') {
await this.setPosition(role, data.position, { reason });
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,
});
}
let icon = data.icon;
@@ -192,17 +202,17 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined;
}
const body = {
const _data = {
name: data.name,
color: typeof data.color === 'undefined' ? undefined : resolveColor(data.color),
hoist: data.hoist,
permissions: typeof data.permissions === 'undefined' ? undefined : new PermissionsBitField(data.permissions),
permissions: typeof data.permissions === 'undefined' ? undefined : new Permissions(data.permissions),
mentionable: data.mentionable,
icon,
unicode_emoji: data.unicodeEmoji,
};
const d = await this.client.api.guilds(this.guild.id).roles(role.id).patch({ body, reason });
const d = await this.client.api.guilds(this.guild.id).roles(role.id).patch({ data: _data, reason });
const clone = role._clone();
clone._patch(d);
@@ -217,54 +227,15 @@ class RoleManager extends CachedManager {
* @example
* // Delete a role
* guild.roles.delete('222079219327434752', 'The role needed to go')
* .then(() => console.log('Deleted the role'))
* .then(deleted => console.log(`Deleted role ${deleted.name}`))
* .catch(console.error);
*/
async delete(role, reason) {
const id = this.resolveId(role);
await this.client.api.guilds(this.guild.id).roles(id).delete({ reason });
await this.client.api.guilds[this.guild.id].roles[id].delete({ reason });
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<Role>}
* @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,
Routes.guildRoles(this.guild.id),
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
@@ -282,7 +253,9 @@ class RoleManager extends CachedManager {
}));
// Call the API to update role positions
await this.client.api.guilds(this.guild.id).roles.patch({ body: rolePositions });
await this.client.api.guilds(this.guild.id).roles.patch({
data: rolePositions,
});
return this.client.actions.GuildRolesPositionUpdate.handle({
guild_id: this.guild.id,
roles: rolePositions,

View File

@@ -1,9 +1,9 @@
'use strict';
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors');
const { StageInstance } = require('../structures/StageInstance');
const { PrivacyLevels } = require('../util/Constants');
/**
* Manages API methods for {@link StageInstance} objects and holds their cache.
@@ -49,7 +49,7 @@ class StageInstanceManager extends CachedManager {
* // Create a stage instance
* guild.stageInstances.create('1234567890123456789', {
* topic: 'A very creative topic',
* privacyLevel: GuildPrivacyLevel.GuildOnly
* privacyLevel: 'GUILD_ONLY'
* })
* .then(stageInstance => console.log(stageInstance))
* .catch(console.error);
@@ -60,8 +60,10 @@ class StageInstanceManager extends CachedManager {
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
let { topic, privacyLevel } = options;
privacyLevel &&= typeof privacyLevel === 'number' ? privacyLevel : PrivacyLevels[privacyLevel];
const data = await this.client.api['stage-instances'].post({
body: {
data: {
channel_id: channelId,
topic,
privacy_level: privacyLevel,
@@ -120,12 +122,14 @@ class StageInstanceManager extends CachedManager {
let { topic, privacyLevel } = options;
privacyLevel &&= typeof privacyLevel === 'number' ? privacyLevel : PrivacyLevels[privacyLevel];
const data = await this.client.api('stage-instances', channelId).patch({
body: {
data: {
topic,
privacy_level: privacyLevel,
},
})
});
if (this.cache.has(data.id)) {
const clone = this.cache.get(data.id)._clone();

View File

@@ -1,10 +1,10 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { ChannelType, Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const ThreadChannel = require('../structures/ThreadChannel');
const { ChannelTypes } = require('../util/Constants');
/**
* Manages API methods for {@link ThreadChannel} objects and stores their cache.
@@ -64,12 +64,12 @@ class ThreadManager extends CachedManager {
* @typedef {StartThreadOptions} ThreadCreateOptions
* @property {MessageResolvable} [startMessage] The message to start a thread from. <warn>If this is defined then type
* of thread gets automatically defined and cannot be changed. The provided `type` field will be ignored</warn>
* @property {ThreadChannelTypes|number} [type] The type of thread to create.
* Defaults to {@link ChannelType.GuildPublicThread} if created in a {@link TextChannel}
* <warn>When creating threads in a {@link NewsChannel} this is ignored and is always
* {@link ChannelType.GuildNewsThread}</warn>
* @property {ThreadChannelTypes|number} [type] The type of thread to create. Defaults to `GUILD_PUBLIC_THREAD` if
* created in a {@link TextChannel} <warn>When creating threads in a {@link NewsChannel} this is ignored and is always
* `GUILD_NEWS_THREAD`</warn>
* @property {boolean} [invitable] Whether non-moderators can add other non-moderators to the thread
* <info>Can only be set when type will be {@link ChannelType.GuildPrivateThread}</info>
* <info>Can only be set when type will be `GUILD_PRIVATE_THREAD`</info>
* @property {number} [rateLimitPerUser] The rate limit per user (slowmode) for the new channel in seconds
*/
/**
@@ -92,7 +92,7 @@ class ThreadManager extends CachedManager {
* .create({
* name: 'mod-talk',
* autoArchiveDuration: 60,
* type: ChannelType.GuildPrivateThread,
* type: 'GUILD_PRIVATE_THREAD',
* reason: 'Needed a separate thread for moderation',
* })
* .then(threadChannel => console.log(threadChannel))
@@ -107,17 +107,18 @@ class ThreadManager extends CachedManager {
reason,
rateLimitPerUser,
} = {}) {
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 === ChannelType.GuildNews ? ChannelType.GuildNewsThread : ChannelType.GuildPublicThread;
let startMessageId;
this.channel.type === 'GUILD_NEWS' ? ChannelTypes.GUILD_NEWS_THREAD : ChannelTypes.GUILD_PUBLIC_THREAD;
if (startMessage) {
startMessageId = this.channel.messages.resolveId(startMessage);
const startMessageId = this.channel.messages.resolveId(startMessage);
if (!startMessageId) throw new TypeError('INVALID_TYPE', 'startMessage', 'MessageResolvable');
} else if (this.channel.type !== ChannelType.GuildNews) {
resolvedType = type ?? resolvedType;
path = path.messages(startMessageId);
} else if (this.channel.type !== 'GUILD_NEWS') {
resolvedType = typeof type === 'string' ? ChannelTypes[type] : type ?? resolvedType;
}
if (autoArchiveDuration === 'MAX') {
autoArchiveDuration = 1440;
@@ -128,12 +129,12 @@ class ThreadManager extends CachedManager {
}
}
const data = await this.client.api.channels(this.channel.id).messages(startMessageId).threads.post({
body: {
const data = await path.threads.post({
data: {
name,
auto_archive_duration: autoArchiveDuration,
type: resolvedType,
invitable: resolvedType === ChannelType.GuildPrivateThread ? invitable : undefined,
invitable: resolvedType === ChannelTypes.GUILD_PRIVATE_THREAD ? invitable : undefined,
rate_limit_per_user: rateLimitPerUser,
},
reason,
@@ -211,31 +212,21 @@ class ThreadManager extends CachedManager {
}
let timestamp;
let id;
const query = new URLSearchParams();
if (typeof before !== 'undefined') {
if (before instanceof ThreadChannel || /^\d{16,19}$/.test(String(before))) {
id = this.resolveId(before);
timestamp = this.resolve(before)?.archivedAt?.toISOString();
const toUse = type === 'private' && !fetchAll ? id : timestamp;
if (toUse) {
query.set('before', toUse);
}
} else {
try {
timestamp = new Date(before).toISOString();
if (type === 'public' || fetchAll) {
query.set('before', timestamp);
}
} catch {
throw new TypeError('INVALID_TYPE', 'before', 'DateResolvable or ThreadChannelResolvable');
}
}
}
if (limit) {
query.set('limit', limit);
}
const raw = await path.threads.archived(type).get({ query });
const raw = await path.threads
.archived(type)
.get({ query: { before: type === 'private' && !fetchAll ? id : timestamp, limit } });
return this.constructor._mapThreads(raw, this.client, { parent: this.channel, cache });
}

View File

@@ -1,7 +1,6 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors');
const ThreadMember = require('../structures/ThreadMember');
@@ -104,22 +103,19 @@ class ThreadMemberManager extends CachedManager {
}
async _fetchMany(cache) {
const raw = await this.client.api.channels(this.thread.id, 'thread-members');
const raw = await this.client.api.channels(this.thread.id, 'thread-members').get();
return raw.reduce((col, member) => col.set(member.user_id, this._add(member, cache)), new Collection());
}
/**
* @typedef {BaseFetchOptions} ThreadMemberFetchOptions
* @property {UserResolvable} [member] The specific user to fetch from the thread
*/
/**
* Fetches member(s) for the thread from Discord, requires access to the `GUILD_MEMBERS` gateway intent.
* @param {ThreadMemberFetchOptions|boolean} [options] Additional options for this fetch, when a `boolean` is provided
* all members are fetched with `options.cache` set to the boolean value
* @param {UserResolvable|boolean} [member] The member to fetch. If `undefined`, all members
* in the thread are fetched, and will be cached based on `options.cache`. If boolean, this serves
* the purpose of `options.cache`.
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<ThreadMember|Collection<Snowflake, ThreadMember>>}
*/
fetch({ member, cache = true, force = false } = {}) {
fetch(member, { cache = true, force = false } = {}) {
const id = this.resolveId(member);
return id ? this._fetchOne(id, cache, force) : this._fetchMany(member ?? cache);
}

View File

@@ -1,6 +1,5 @@
'use strict';
const { ChannelType, Routes } = require('discord-api-types/v9');
const CachedManager = require('./CachedManager');
const { GuildMember } = require('../structures/GuildMember');
const { Message } = require('../structures/Message');
@@ -39,7 +38,7 @@ class UserManager extends CachedManager {
* @private
*/
dmChannel(userId) {
return this.client.channels.cache.find(c => c.type === ChannelType.DM && c.recipient.id === userId) ?? null;
return this.client.channels.cache.find(c => c.type === 'DM' && c.recipient.id === userId) ?? null;
}
/**
@@ -56,7 +55,11 @@ class UserManager extends CachedManager {
if (dmChannel && !dmChannel.partial) return dmChannel;
}
const data = await this.client.api.users('@me').channels.post({ body: { recipient_id: id } });
const data = await this.client.api.users(this.client.user.id).channels.post({
data: {
recipient_id: id,
},
});
return this.client.channels._add(data, null, { cache });
}
@@ -69,7 +72,7 @@ class UserManager extends CachedManager {
const id = this.resolveId(user);
const dmChannel = this.dmChannel(id);
if (!dmChannel) throw new Error('USER_NO_DM_CHANNEL');
await this.client.channels(dmChannel.id).delete();
await this.client.api.channels(dmChannel.id).delete();
this.client.channels._remove(dmChannel.id);
return dmChannel;
}
@@ -97,7 +100,7 @@ class UserManager extends CachedManager {
* Fetches a user's flags.
* @param {UserResolvable} user The UserResolvable to identify
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<UserFlagsBitField>}
* @returns {Promise<UserFlags>}
*/
async fetchFlags(user, options) {
return (await this.fetch(user, options)).flags;