mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
refactor: use @discordjs/rest (#7298)
Co-authored-by: ckohen <chaikohen@gmail.com>
This commit is contained in:
@@ -49,12 +49,10 @@
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^0.12.0",
|
||||
"@discordjs/collection": "^0.5.0",
|
||||
"@sapphire/async-queue": "^1.1.9",
|
||||
"@discordjs/rest": "^0.3.0",
|
||||
"@sapphire/snowflake": "^3.0.1",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/ws": "^8.2.2",
|
||||
"discord-api-types": "^0.26.1",
|
||||
"form-data": "^4.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"ws": "^8.4.2"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('node:events');
|
||||
const { clearInterval } = require('node:timers');
|
||||
const { REST } = require('@discordjs/rest');
|
||||
const { TypeError } = require('../errors');
|
||||
const RESTManager = require('../rest/RESTManager');
|
||||
const Options = require('../util/Options');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
@@ -27,20 +26,9 @@ class BaseClient extends EventEmitter {
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
* @type {RESTManager}
|
||||
* @private
|
||||
* @type {REST}
|
||||
*/
|
||||
this.rest = new RESTManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* API shortcut
|
||||
* @type {Object}
|
||||
* @readonly
|
||||
* @private
|
||||
*/
|
||||
get api() {
|
||||
return this.rest.api;
|
||||
this.rest = new REST(this.options.rest);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +36,8 @@ class BaseClient extends EventEmitter {
|
||||
* @returns {void}
|
||||
*/
|
||||
destroy() {
|
||||
if (this.rest.sweepInterval) clearInterval(this.rest.sweepInterval);
|
||||
this.rest.requestManager.clearHashSweeper();
|
||||
this.rest.requestManager.clearHandlerSweeper();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +70,6 @@ class BaseClient extends EventEmitter {
|
||||
module.exports = BaseClient;
|
||||
|
||||
/**
|
||||
* Emitted for general debugging information.
|
||||
* @event BaseClient#debug
|
||||
* @param {string} info The debug information
|
||||
* @external REST
|
||||
* @see {@link https://discord.js.org/#/docs/rest/main/class/REST}
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const process = require('node:process');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const BaseClient = require('./BaseClient');
|
||||
const ActionsManager = require('./actions/ActionsManager');
|
||||
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
||||
@@ -210,6 +211,7 @@ class Client extends BaseClient {
|
||||
async login(token = this.token) {
|
||||
if (!token || typeof token !== 'string') throw new Error('TOKEN_INVALID');
|
||||
this.token = token = token.replace(/^(Bot|Bearer)\s*/i, '');
|
||||
this.rest.setToken(token);
|
||||
this.emit(
|
||||
Events.DEBUG,
|
||||
`Provided token: ${token
|
||||
@@ -252,6 +254,7 @@ class Client extends BaseClient {
|
||||
this.sweepers.destroy();
|
||||
this.ws.destroy();
|
||||
this.token = null;
|
||||
this.rest.setToken(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,9 +276,14 @@ class Client extends BaseClient {
|
||||
*/
|
||||
async fetchInvite(invite, options) {
|
||||
const code = DataResolver.resolveInviteCode(invite);
|
||||
const data = await this.api.invites(code).get({
|
||||
query: { with_counts: true, with_expiration: true, guild_scheduled_event_id: options?.guildScheduledEventId },
|
||||
const query = new URLSearchParams({
|
||||
with_counts: true,
|
||||
with_expiration: true,
|
||||
});
|
||||
if (options?.guildScheduledEventId) {
|
||||
query.set('guild_scheduled_event_id', options.guildScheduledEventId);
|
||||
}
|
||||
const data = await this.rest.get(Routes.invite(code), { query });
|
||||
return new Invite(this, data);
|
||||
}
|
||||
|
||||
@@ -290,7 +298,7 @@ class Client extends BaseClient {
|
||||
*/
|
||||
async fetchGuildTemplate(template) {
|
||||
const code = DataResolver.resolveGuildTemplateCode(template);
|
||||
const data = await this.api.guilds.templates(code).get();
|
||||
const data = await this.rest.get(Routes.template(code));
|
||||
return new GuildTemplate(this, data);
|
||||
}
|
||||
|
||||
@@ -305,7 +313,7 @@ class Client extends BaseClient {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchWebhook(id, token) {
|
||||
const data = await this.api.webhooks(id, token).get();
|
||||
const data = await this.rest.get(Routes.webhook(id, token));
|
||||
return new Webhook(this, { token, ...data });
|
||||
}
|
||||
|
||||
@@ -318,7 +326,7 @@ class Client extends BaseClient {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchVoiceRegions() {
|
||||
const apiRegions = await this.api.voice.regions.get();
|
||||
const apiRegions = await this.rest.get(Routes.voiceRegions());
|
||||
const regions = new Collection();
|
||||
for (const region of apiRegions) regions.set(region.id, new VoiceRegion(region));
|
||||
return regions;
|
||||
@@ -334,7 +342,7 @@ class Client extends BaseClient {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchSticker(id) {
|
||||
const data = await this.api.stickers(id).get();
|
||||
const data = await this.rest.get(Routes.sticker(id));
|
||||
return new Sticker(this, data);
|
||||
}
|
||||
|
||||
@@ -347,7 +355,7 @@ class Client extends BaseClient {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchPremiumStickerPacks() {
|
||||
const data = await this.api('sticker-packs').get();
|
||||
const data = await this.rest.get(Routes.nitroStickerPacks());
|
||||
return new Collection(data.sticker_packs.map(p => [p.id, new StickerPack(this, p)]));
|
||||
}
|
||||
|
||||
@@ -359,7 +367,7 @@ class Client extends BaseClient {
|
||||
async fetchGuildPreview(guild) {
|
||||
const id = this.guilds.resolveId(guild);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
|
||||
const data = await this.api.guilds(id).preview.get();
|
||||
const data = await this.rest.get(Routes.guildPreview(id));
|
||||
return new GuildPreview(this, data);
|
||||
}
|
||||
|
||||
@@ -371,7 +379,7 @@ class Client extends BaseClient {
|
||||
async fetchGuildWidget(guild) {
|
||||
const id = this.guilds.resolveId(guild);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'guild', 'GuildResolvable');
|
||||
const data = await this.api.guilds(id, 'widget.json').get();
|
||||
const data = await this.rest.get(Routes.guildWidgetJSON(id));
|
||||
return new Widget(this, data);
|
||||
}
|
||||
|
||||
@@ -443,7 +451,7 @@ class Client extends BaseClient {
|
||||
query.set('guild_id', guildId);
|
||||
}
|
||||
|
||||
return `${this.options.http.api}${this.api.oauth2.authorize}?${query}`;
|
||||
return `${this.options.rest.api}${Routes.oauth2Authorization()}?${query}`;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
@@ -487,39 +495,15 @@ class Client extends BaseClient {
|
||||
if (typeof options.sweepers !== 'object' || options.sweepers === null) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object');
|
||||
}
|
||||
if (typeof options.invalidRequestWarningInterval !== 'number' || isNaN(options.invalidRequestWarningInterval)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'invalidRequestWarningInterval', 'a number');
|
||||
}
|
||||
if (!Array.isArray(options.partials)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');
|
||||
}
|
||||
if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number');
|
||||
}
|
||||
if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number');
|
||||
}
|
||||
if (typeof options.restGlobalRateLimit !== 'number' || isNaN(options.restGlobalRateLimit)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restGlobalRateLimit', 'a number');
|
||||
}
|
||||
if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number');
|
||||
}
|
||||
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
|
||||
}
|
||||
if (typeof options.failIfNotExists !== 'boolean') {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'failIfNotExists', 'a boolean');
|
||||
}
|
||||
if (!Array.isArray(options.userAgentSuffix)) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'userAgentSuffix', 'an array of strings');
|
||||
}
|
||||
if (
|
||||
typeof options.rejectOnRateLimit !== 'undefined' &&
|
||||
!(typeof options.rejectOnRateLimit === 'function' || Array.isArray(options.rejectOnRateLimit))
|
||||
) {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'rejectOnRateLimit', 'an array or a function');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +522,12 @@ module.exports = Client;
|
||||
* @typedef {string} Snowflake
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted for general debugging information.
|
||||
* @event Client#debug
|
||||
* @param {string} info The debug information
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted for general warnings.
|
||||
* @event Client#warn
|
||||
@@ -548,3 +538,8 @@ module.exports = Client;
|
||||
* @external Collection
|
||||
* @see {@link https://discord.js.org/#/docs/collection/main/class/Collection}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external ImageURLOptions
|
||||
* @see {@link https://discord.js.org/#/docs/rest/main/typedef/ImageURLOptions}
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ const EventEmitter = require('node:events');
|
||||
const { setImmediate } = require('node:timers');
|
||||
const { setTimeout: sleep } = require('node:timers/promises');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { RPCErrorCodes } = require('discord-api-types/v9');
|
||||
const { Routes, RPCErrorCodes } = require('discord-api-types/v9');
|
||||
const WebSocketShard = require('./WebSocketShard');
|
||||
const PacketHandlers = require('./handlers');
|
||||
const { Error } = require('../../errors');
|
||||
@@ -131,7 +131,7 @@ class WebSocketManager extends EventEmitter {
|
||||
url: gatewayURL,
|
||||
shards: recommendedShards,
|
||||
session_start_limit: sessionStartLimit,
|
||||
} = await this.client.api.gateway.bot.get().catch(error => {
|
||||
} = await this.client.rest.get(Routes.gatewayBot()).catch(error => {
|
||||
throw error.httpStatus === 401 ? invalidToken : error;
|
||||
});
|
||||
|
||||
|
||||
@@ -16,16 +16,13 @@ exports.BitField = require('./util/BitField');
|
||||
exports.Collection = require('@discordjs/collection').Collection;
|
||||
exports.Constants = require('./util/Constants');
|
||||
exports.DataResolver = require('./util/DataResolver');
|
||||
exports.DiscordAPIError = require('./rest/DiscordAPIError');
|
||||
exports.EnumResolvers = require('./util/EnumResolvers');
|
||||
exports.Formatters = require('./util/Formatters');
|
||||
exports.HTTPError = require('./rest/HTTPError');
|
||||
exports.Intents = require('./util/Intents');
|
||||
exports.LimitedCollection = require('./util/LimitedCollection');
|
||||
exports.MessageFlags = require('./util/MessageFlags');
|
||||
exports.Options = require('./util/Options');
|
||||
exports.Permissions = require('./util/Permissions');
|
||||
exports.RateLimitError = require('./rest/RateLimitError');
|
||||
exports.SnowflakeUtil = require('@sapphire/snowflake').DiscordSnowflake;
|
||||
exports.Sweepers = require('./util/Sweepers');
|
||||
exports.SystemChannelFlags = require('./util/SystemChannelFlags');
|
||||
@@ -181,3 +178,6 @@ exports.ActionRow = require('@discordjs/builders').ActionRow;
|
||||
exports.ButtonComponent = require('@discordjs/builders').ButtonComponent;
|
||||
exports.SelectMenuComponent = require('@discordjs/builders').SelectMenuComponent;
|
||||
exports.SelectMenuOption = require('@discordjs/builders').SelectMenuOption;
|
||||
exports.DiscordAPIError = require('@discordjs/rest').DiscordAPIError;
|
||||
exports.HTTPError = require('@discordjs/rest').HTTPError;
|
||||
exports.RateLimitError = require('@discordjs/rest').RateLimitError;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -36,13 +37,23 @@ 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 {Object}
|
||||
* @returns {string}
|
||||
* @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);
|
||||
return id ? path.commands(id) : path.commands;
|
||||
if (this.guild ?? guildId) {
|
||||
if (id) {
|
||||
return Routes.applicationGuildCommand(this.client.application.id, this.guild?.id ?? guildId, id);
|
||||
}
|
||||
|
||||
return Routes.applicationGuildCommands(this.client.application.id, this.guild?.id ?? guildId);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
return Routes.applicationCommand(this.client.application.id, id);
|
||||
}
|
||||
|
||||
return Routes.applicationCommands(this.client.application.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,11 +100,11 @@ class ApplicationCommandManager extends CachedManager {
|
||||
const existing = this.cache.get(id);
|
||||
if (existing) return existing;
|
||||
}
|
||||
const command = await this.commandPath({ id, guildId }).get();
|
||||
const command = await this.client.rest.get(this.commandPath({ id, guildId }));
|
||||
return this._add(command, cache);
|
||||
}
|
||||
|
||||
const data = await this.commandPath({ guildId }).get();
|
||||
const data = await this.client.rest.get(this.commandPath({ guildId }));
|
||||
return data.reduce((coll, command) => coll.set(command.id, this._add(command, cache, guildId)), new Collection());
|
||||
}
|
||||
|
||||
@@ -113,8 +124,8 @@ class ApplicationCommandManager extends CachedManager {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async create(command, guildId) {
|
||||
const data = await this.commandPath({ guildId }).post({
|
||||
data: this.constructor.transformCommand(command),
|
||||
const data = await this.client.rest.post(this.commandPath({ guildId }), {
|
||||
body: this.constructor.transformCommand(command),
|
||||
});
|
||||
return this._add(data, true, guildId);
|
||||
}
|
||||
@@ -142,8 +153,8 @@ class ApplicationCommandManager extends CachedManager {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async set(commands, guildId) {
|
||||
const data = await this.commandPath({ guildId }).put({
|
||||
data: commands.map(c => this.constructor.transformCommand(c)),
|
||||
const data = await this.client.rest.put(this.commandPath({ guildId }), {
|
||||
body: commands.map(c => this.constructor.transformCommand(c)),
|
||||
});
|
||||
return data.reduce((coll, command) => coll.set(command.id, this._add(command, true, guildId)), new Collection());
|
||||
}
|
||||
@@ -167,8 +178,8 @@ class ApplicationCommandManager extends CachedManager {
|
||||
const id = this.resolveId(command);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
||||
|
||||
const patched = await this.commandPath({ id, guildId }).patch({
|
||||
data: this.constructor.transformCommand(data),
|
||||
const patched = await this.client.rest.patch(this.commandPath({ id, guildId }), {
|
||||
body: this.constructor.transformCommand(data),
|
||||
});
|
||||
return this._add(patched, true, guildId);
|
||||
}
|
||||
@@ -189,7 +200,7 @@ class ApplicationCommandManager extends CachedManager {
|
||||
const id = this.resolveId(command);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
||||
|
||||
await this.commandPath({ id, guildId }).delete();
|
||||
await this.client.rest.delete(this.commandPath({ id, guildId }));
|
||||
|
||||
const cached = this.cache.get(id);
|
||||
this.cache.delete(id);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { RESTJSONErrorCodes } = require('discord-api-types/v9');
|
||||
const { RESTJSONErrorCodes, Routes } = require('discord-api-types/v9');
|
||||
const BaseManager = require('./BaseManager');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
|
||||
@@ -43,11 +43,15 @@ 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 {Object}
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
permissionsPath(guildId, commandId) {
|
||||
return this.client.api.applications(this.client.application.id).guilds(guildId).commands(commandId).permissions;
|
||||
if (commandId) {
|
||||
return Routes.applicationCommandPermissions(this.client.application.id, guildId, commandId);
|
||||
}
|
||||
|
||||
return Routes.guildApplicationCommandsPermissions(this.client.application.id, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,11 +99,11 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
||||
async fetch({ guild, command } = {}) {
|
||||
const { guildId, commandId } = this._validateOptions(guild, command);
|
||||
if (commandId) {
|
||||
const data = await this.permissionsPath(guildId, commandId).get();
|
||||
const data = await this.client.rest.get(this.permissionsPath(guildId, commandId));
|
||||
return data.permissions;
|
||||
}
|
||||
|
||||
const data = await this.permissionsPath(guildId).get();
|
||||
const data = await this.client.rest.get(this.permissionsPath(guildId));
|
||||
return data.reduce((coll, perm) => coll.set(perm.id, perm.permissions), new Collection());
|
||||
}
|
||||
|
||||
@@ -158,7 +162,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
||||
if (!Array.isArray(permissions)) {
|
||||
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
|
||||
}
|
||||
const data = await this.permissionsPath(guildId, commandId).put({ data: { permissions } });
|
||||
const data = await this.client.rest.put(this.permissionsPath(guildId, commandId), { body: { permissions } });
|
||||
return data.permissions;
|
||||
}
|
||||
|
||||
@@ -166,7 +170,7 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
||||
throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true);
|
||||
}
|
||||
|
||||
const data = await this.permissionsPath(guildId).put({ data: fullPermissions });
|
||||
const data = await this.client.rest.put(this.permissionsPath(guildId), { body: fullPermissions });
|
||||
return data.reduce((coll, perm) => coll.set(perm.id, perm.permissions), new Collection());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const process = require('node:process');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { Channel } = require('../structures/Channel');
|
||||
const { Events, ThreadChannelTypes } = require('../util/Constants');
|
||||
@@ -112,7 +113,7 @@ class ChannelManager extends CachedManager {
|
||||
if (existing && !existing.partial) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.channels(id).get();
|
||||
const data = await this.client.rest.get(Routes.channel(id));
|
||||
return this._add(data, null, { cache, allowUnknownGuild });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -107,12 +108,12 @@ class GuildBanManager extends CachedManager {
|
||||
if (existing && !existing.partial) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).bans(user).get();
|
||||
const data = await this.client.rest.get(Routes.guildBan(this.guild.id, user));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
async _fetchMany(cache) {
|
||||
const data = await this.client.api.guilds(this.guild.id).bans.get();
|
||||
const data = await this.client.rest.get(Routes.guildBans(this.guild.id));
|
||||
return data.reduce((col, ban) => col.set(ban.user.id, this._add(ban, cache)), new Collection());
|
||||
}
|
||||
|
||||
@@ -140,13 +141,10 @@ class GuildBanManager extends CachedManager {
|
||||
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({
|
||||
data: { delete_message_days: options.days },
|
||||
reason: options.reason,
|
||||
});
|
||||
await this.client.rest.put(Routes.guildBan(this.guild.id, id), {
|
||||
body: { delete_message_days: options.days },
|
||||
reason: options.reason,
|
||||
});
|
||||
if (user instanceof GuildMember) return user;
|
||||
const _user = this.client.users.resolve(id);
|
||||
if (_user) {
|
||||
@@ -169,7 +167,7 @@ class GuildBanManager extends CachedManager {
|
||||
async remove(user, reason) {
|
||||
const id = this.client.users.resolveId(user);
|
||||
if (!id) throw new Error('BAN_RESOLVE_ID');
|
||||
await this.client.api.guilds(this.guild.id).bans(id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildBan(this.guild.id, id), { reason });
|
||||
return this.client.users.resolve(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const process = require('node:process');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const ThreadManager = require('./ThreadManager');
|
||||
const { Error } = require('../errors');
|
||||
@@ -150,8 +150,8 @@ class GuildChannelManager extends CachedManager {
|
||||
);
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).channels.post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.guildChannels(this.guild.id), {
|
||||
body: {
|
||||
name,
|
||||
topic,
|
||||
type,
|
||||
@@ -192,13 +192,13 @@ class GuildChannelManager extends CachedManager {
|
||||
}
|
||||
|
||||
if (id) {
|
||||
const data = await this.client.api.channels(id).get();
|
||||
const data = await this.client.rest.get(Routes.channel(id));
|
||||
// Since this is the guild manager, throw if on a different guild
|
||||
if (this.guild.id !== data.guild_id) throw new Error('GUILD_CHANNEL_UNOWNED');
|
||||
return this.client.channels._add(data, this.guild, { cache });
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).channels.get();
|
||||
const data = await this.client.rest.get(Routes.guildChannels(this.guild.id));
|
||||
const channels = new Collection();
|
||||
for (const channel of data) channels.set(channel.id, this.client.channels._add(channel, this.guild, { cache }));
|
||||
return channels;
|
||||
@@ -238,7 +238,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.patch({ data: channelPositions });
|
||||
await this.client.rest.patch(Routes.guildChannels(this.guild.id), { body: channelPositions });
|
||||
return this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
channels: channelPositions,
|
||||
@@ -256,7 +256,7 @@ class GuildChannelManager extends CachedManager {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchActiveThreads(cache = true) {
|
||||
const raw = await this.client.api.guilds(this.guild.id).threads.active.get();
|
||||
const raw = await this.client.rest.get(Routes.guildActiveThreads(this.guild.id));
|
||||
return ThreadManager._mapThreads(raw, this.client, { guild: this.guild, cache });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const BaseGuildEmojiManager = require('./BaseGuildEmojiManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
@@ -52,20 +53,20 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||
attachment = await DataResolver.resolveImage(attachment);
|
||||
if (!attachment) throw new TypeError('REQ_RESOURCE_TYPE');
|
||||
|
||||
const data = { image: attachment, name };
|
||||
const body = { 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);
|
||||
}
|
||||
data.roles = [];
|
||||
body.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);
|
||||
data.roles.push(resolvedRole);
|
||||
body.roles.push(resolvedRole);
|
||||
}
|
||||
}
|
||||
|
||||
const emoji = await this.client.api.guilds(this.guild.id).emojis.post({ data, reason });
|
||||
const emoji = await this.client.rest.post(Routes.guildEmojis(this.guild.id), { body, reason });
|
||||
return this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji;
|
||||
}
|
||||
|
||||
@@ -91,11 +92,11 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||
const existing = this.cache.get(id);
|
||||
if (existing) return existing;
|
||||
}
|
||||
const emoji = await this.client.api.guilds(this.guild.id).emojis(id).get();
|
||||
const emoji = await this.client.rest.get(Routes.guildEmoji(this.guild.id, id));
|
||||
return this._add(emoji, cache);
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).emojis.get();
|
||||
const data = await this.client.rest.get(Routes.guildEmojis(this.guild.id));
|
||||
const emojis = new Collection();
|
||||
for (const emoji of data) emojis.set(emoji.id, this._add(emoji, cache));
|
||||
return emojis;
|
||||
@@ -110,7 +111,7 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||
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 });
|
||||
await this.client.rest.delete(Routes.guildEmoji(this.guild.id, id), { reason });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,16 +125,13 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
||||
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 newData = await this.client.rest.patch(Routes.guildEmoji(this.guild.id, id), {
|
||||
body: {
|
||||
name: data.name,
|
||||
roles,
|
||||
},
|
||||
reason,
|
||||
});
|
||||
const existing = this.cache.get(id);
|
||||
if (existing) {
|
||||
const clone = existing._clone();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -155,12 +156,12 @@ class GuildInviteManager extends CachedManager {
|
||||
}
|
||||
|
||||
async _fetchMany(cache) {
|
||||
const data = await this.client.api.guilds(this.guild.id).invites.get();
|
||||
const data = await this.client.rest.get(Routes.guildInvites(this.guild.id));
|
||||
return data.reduce((col, invite) => col.set(invite.code, this._add(invite, cache)), new Collection());
|
||||
}
|
||||
|
||||
async _fetchChannelMany(channelId, cache) {
|
||||
const data = await this.client.api.channels(channelId).invites.get();
|
||||
const data = await this.client.rest.get(Routes.channelInvites(channelId));
|
||||
return data.reduce((col, invite) => col.set(invite.code, this._add(invite, cache)), new Collection());
|
||||
}
|
||||
|
||||
@@ -182,8 +183,8 @@ class GuildInviteManager extends CachedManager {
|
||||
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({
|
||||
data: {
|
||||
const invite = await this.client.rest.post(Routes.channelInvites(id), {
|
||||
body: {
|
||||
temporary,
|
||||
max_age: maxAge,
|
||||
max_uses: maxUses,
|
||||
@@ -206,7 +207,7 @@ class GuildInviteManager extends CachedManager {
|
||||
async delete(invite, reason) {
|
||||
const code = DataResolver.resolveInviteCode(invite);
|
||||
|
||||
await this.client.api.invites(code).delete({ reason });
|
||||
await this.client.rest.delete(Routes.invite(code), { reason });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const process = require('node:process');
|
||||
const { setTimeout, clearTimeout } = 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');
|
||||
@@ -199,8 +200,8 @@ class GuildManager extends CachedManager {
|
||||
}
|
||||
systemChannelFlags &&= SystemChannelFlags.resolve(systemChannelFlags);
|
||||
|
||||
const data = await this.client.api.guilds.post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.guilds(), {
|
||||
body: {
|
||||
name,
|
||||
icon,
|
||||
verification_level: verificationLevel,
|
||||
@@ -266,11 +267,13 @@ class GuildManager extends CachedManager {
|
||||
if (existing) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(id).get({ query: { with_counts: options.withCounts ?? true } });
|
||||
const data = await this.client.rest.get(Routes.guild(id), {
|
||||
query: new URLSearchParams({ with_counts: options.withCounts ?? true }),
|
||||
});
|
||||
return this._add(data, options.cache);
|
||||
}
|
||||
|
||||
const data = await this.client.api.users('@me').guilds.get({ query: options });
|
||||
const data = await this.client.rest.get(Routes.userGuilds(), { query: new URLSearchParams(options) });
|
||||
return data.reduce((coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)), new Collection());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ const { Buffer } = require('node:buffer');
|
||||
const { setTimeout, clearTimeout } = require('node:timers');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { Error, TypeError, RangeError } = require('../errors');
|
||||
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
|
||||
@@ -113,7 +114,7 @@ class GuildMemberManager extends CachedManager {
|
||||
}
|
||||
resolvedOptions.roles = resolvedRoles;
|
||||
}
|
||||
const data = await this.client.api.guilds(this.guild.id).members(userId).put({ data: resolvedOptions });
|
||||
const data = await this.client.rest.put(Routes.guildMember(this.guild.id, userId), { body: 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);
|
||||
}
|
||||
@@ -203,7 +204,9 @@ class GuildMemberManager extends CachedManager {
|
||||
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
||||
*/
|
||||
async search({ query, limit = 1, cache = true } = {}) {
|
||||
const data = await this.client.api.guilds(this.guild.id).members.search.get({ query: { query, limit } });
|
||||
const data = await this.client.rest.get(Routes.guildMembersSearch(this.guild.id), {
|
||||
query: new URLSearchParams({ query, limit }),
|
||||
});
|
||||
return data.reduce((col, member) => col.set(member.user.id, this._add(member, cache)), new Collection());
|
||||
}
|
||||
|
||||
@@ -221,7 +224,11 @@ class GuildMemberManager extends CachedManager {
|
||||
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
||||
*/
|
||||
async list({ after, limit = 1, cache = true } = {}) {
|
||||
const data = await this.client.api.guilds(this.guild.id).members.get({ query: { after, limit } });
|
||||
const query = new URLSearchParams({ limit });
|
||||
if (after) {
|
||||
query.set('after', after);
|
||||
}
|
||||
const data = await this.client.rest.get(Routes.guildMembers(this.guild.id), { query });
|
||||
return data.reduce((col, member) => col.set(member.user.id, this._add(member, cache)), new Collection());
|
||||
}
|
||||
|
||||
@@ -271,13 +278,13 @@ class GuildMemberManager extends CachedManager {
|
||||
? new Date(_data.communicationDisabledUntil).toISOString()
|
||||
: _data.communicationDisabledUntil;
|
||||
|
||||
let endpoint = this.client.api.guilds(this.guild.id);
|
||||
let endpoint;
|
||||
if (id === this.client.user.id) {
|
||||
const keys = Object.keys(data);
|
||||
if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me');
|
||||
else endpoint = endpoint.members(id);
|
||||
if (keys.length === 1 && keys[0] === 'nick') endpoint = Routes.guildMember(this.guild.id);
|
||||
else endpoint = Routes.guildMember(this.guild.id, id);
|
||||
} else {
|
||||
endpoint = endpoint.members(id);
|
||||
endpoint = Routes.guildMember(this.guild.id, id);
|
||||
}
|
||||
const d = await endpoint.patch({ data: _data, reason });
|
||||
|
||||
@@ -336,11 +343,11 @@ class GuildMemberManager extends CachedManager {
|
||||
query.include_roles = dry ? resolvedRoles.join(',') : resolvedRoles;
|
||||
}
|
||||
|
||||
const endpoint = this.client.api.guilds(this.guild.id).prune;
|
||||
const endpoint = Routes.guildPrune(this.guild.id);
|
||||
|
||||
const { pruned } = await (dry
|
||||
? endpoint.get({ query, reason })
|
||||
: endpoint.post({ data: { ...query, compute_prune_count }, reason }));
|
||||
? this.client.rest.get(endpoint, { query: new URLSearchParams(query), reason })
|
||||
: this.client.rest.post(endpoint, { body: { ...query, compute_prune_count }, reason }));
|
||||
|
||||
return pruned;
|
||||
}
|
||||
@@ -363,7 +370,7 @@ class GuildMemberManager extends CachedManager {
|
||||
const id = this.client.users.resolveId(user);
|
||||
if (!id) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable'));
|
||||
|
||||
await this.client.api.guilds(this.guild.id).members(id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildMember(this.guild.id, id), { reason });
|
||||
|
||||
return this.resolve(user) ?? this.client.users.resolve(user) ?? id;
|
||||
}
|
||||
@@ -407,7 +414,7 @@ class GuildMemberManager extends CachedManager {
|
||||
if (existing && !existing.partial) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).members(user).get();
|
||||
const data = await this.client.rest.get(Routes.guildMember(this.guild.id, user));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -121,7 +122,7 @@ class GuildMemberRoleManager extends DataManager {
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes');
|
||||
}
|
||||
|
||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].put({ reason });
|
||||
await this.client.rest.put(Routes.guildMemberRole(this.guild.id, this.member.id, roleOrRoles), { reason });
|
||||
|
||||
const clone = this.member._clone();
|
||||
clone._roles = [...this.cache.keys(), roleOrRoles];
|
||||
@@ -152,7 +153,7 @@ class GuildMemberRoleManager extends DataManager {
|
||||
throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes');
|
||||
}
|
||||
|
||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildMemberRole(this.guild.id, this.member.id, roleOrRoles), { reason });
|
||||
|
||||
const clone = this.member._clone();
|
||||
const newRoles = this.cache.filter(role => role.id !== roleOrRoles);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { GuildScheduledEventEntityType } = require('discord-api-types/v9');
|
||||
const { GuildScheduledEventEntityType, Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { TypeError, Error } = require('../errors');
|
||||
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
|
||||
@@ -89,8 +89,8 @@ class GuildScheduledEventManager extends CachedManager {
|
||||
entity_metadata = typeof entityMetadata === 'undefined' ? entityMetadata : null;
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events').post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.guildScheduledEvents(this.guild.id), {
|
||||
body: {
|
||||
channel_id,
|
||||
name,
|
||||
privacy_level: privacyLevel,
|
||||
@@ -136,15 +136,15 @@ class GuildScheduledEventManager extends CachedManager {
|
||||
if (existing) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api
|
||||
.guilds(this.guild.id, 'scheduled-events', id)
|
||||
.get({ query: { with_user_count: options.withUserCount ?? true } });
|
||||
const data = await this.client.rest.get(Routes.guildScheduledEvent(this.guild.id, id), {
|
||||
query: new URLSearchParams({ 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: { with_user_count: options.withUserCount ?? true } });
|
||||
const data = await this.client.rest.get(Routes.guildScheduledEvents(this.guild.id), {
|
||||
query: new URLSearchParams({ with_user_count: options.withUserCount ?? true }),
|
||||
});
|
||||
|
||||
return data.reduce(
|
||||
(coll, rawGuildScheduledEventData) =>
|
||||
@@ -205,8 +205,8 @@ class GuildScheduledEventManager extends CachedManager {
|
||||
};
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).patch({
|
||||
data: {
|
||||
const data = await this.client.rest.patch(Routes.guildScheduledEvent(this.guild.id, guildScheduledEventId), {
|
||||
body: {
|
||||
channel_id: typeof channel === 'undefined' ? channel : this.guild.channels.resolveId(channel),
|
||||
name,
|
||||
privacy_level: privacyLevel,
|
||||
@@ -232,7 +232,7 @@ class GuildScheduledEventManager extends CachedManager {
|
||||
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
||||
if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
||||
|
||||
await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).delete();
|
||||
await this.client.rest.delete(Routes.guildScheduledEvent(this.guild.id, guildScheduledEventId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,8 +265,26 @@ class GuildScheduledEventManager extends CachedManager {
|
||||
|
||||
let { limit, withMember, before, after } = options;
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).users.get({
|
||||
query: { limit, with_member: withMember, before, after },
|
||||
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.rest.get(Routes.guildScheduledEventUsers(this.guild.id, guildScheduledEventId), {
|
||||
query,
|
||||
});
|
||||
|
||||
return data.reduce(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -61,11 +62,14 @@ class GuildStickerManager extends CachedManager {
|
||||
if (!resolvedFile) throw new TypeError('REQ_RESOURCE_TYPE');
|
||||
file = { ...resolvedFile, key: 'file' };
|
||||
|
||||
const data = { name, tags, description: description ?? '' };
|
||||
const body = { name, tags, description: description ?? '' };
|
||||
|
||||
const sticker = await this.client.api
|
||||
.guilds(this.guild.id)
|
||||
.stickers.post({ data, files: [file], reason, dontUsePayloadJSON: true });
|
||||
const sticker = await this.client.rest.post(Routes.guildStickers(this.guild.id), {
|
||||
appendToFormData: true,
|
||||
body,
|
||||
files: [file],
|
||||
reason,
|
||||
});
|
||||
return this.client.actions.GuildStickerCreate.handle(this.guild, sticker).sticker;
|
||||
}
|
||||
|
||||
@@ -105,8 +109,8 @@ class GuildStickerManager extends CachedManager {
|
||||
const stickerId = this.resolveId(sticker);
|
||||
if (!stickerId) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
||||
|
||||
const d = await this.client.api.guilds(this.guild.id).stickers(stickerId).patch({
|
||||
data,
|
||||
const d = await this.client.rest.patch(Routes.guildSticker(this.guild.id, stickerId), {
|
||||
body: data,
|
||||
reason,
|
||||
});
|
||||
|
||||
@@ -129,7 +133,7 @@ class GuildStickerManager extends CachedManager {
|
||||
sticker = this.resolveId(sticker);
|
||||
if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
||||
|
||||
await this.client.api.guilds(this.guild.id).stickers(sticker).delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildSticker(this.guild.id, sticker), { reason });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,11 +158,11 @@ class GuildStickerManager extends CachedManager {
|
||||
const existing = this.cache.get(id);
|
||||
if (existing) return existing;
|
||||
}
|
||||
const sticker = await this.client.api.guilds(this.guild.id).stickers(id).get();
|
||||
const sticker = await this.client.rest.get(Routes.guildSticker(this.guild.id, id));
|
||||
return this._add(sticker, cache);
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).stickers.get();
|
||||
const data = await this.client.rest.get(Routes.guildStickers(this.guild.id));
|
||||
return new Collection(data.map(sticker => [sticker.id, this._add(sticker, cache)]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -82,7 +83,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.rest.get(Routes.channelPins(this.channel.id));
|
||||
const messages = new Collection();
|
||||
for (const message of data) messages.set(message.id, this._add(message, cache));
|
||||
return messages;
|
||||
@@ -123,13 +124,13 @@ class MessageManager extends CachedManager {
|
||||
const messageId = this.resolveId(message);
|
||||
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||
|
||||
const { data, files } = await (options instanceof MessagePayload
|
||||
const { body, files } = await (options instanceof MessagePayload
|
||||
? options
|
||||
: MessagePayload.create(message instanceof Message ? message : this, options)
|
||||
)
|
||||
.resolveData()
|
||||
.resolveBody()
|
||||
.resolveFiles();
|
||||
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
|
||||
const d = await this.client.rest.patch(Routes.channelMessage(this.channel.id, messageId), { body, files });
|
||||
|
||||
const existing = this.cache.get(messageId);
|
||||
if (existing) {
|
||||
@@ -149,7 +150,7 @@ class MessageManager extends CachedManager {
|
||||
message = this.resolveId(message);
|
||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||
|
||||
const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
|
||||
const data = await this.client.rest.post(Routes.channelMessageCrosspost(this.channel.id, message));
|
||||
return this.cache.get(data.id) ?? this._add(data);
|
||||
}
|
||||
|
||||
@@ -162,7 +163,7 @@ class MessageManager extends CachedManager {
|
||||
message = this.resolveId(message);
|
||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||
|
||||
await this.client.api.channels(this.channel.id).pins(message).put();
|
||||
await this.client.rest.put(Routes.channelPins(this.channel.id, message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,7 +175,7 @@ class MessageManager extends CachedManager {
|
||||
message = this.resolveId(message);
|
||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||
|
||||
await this.client.api.channels(this.channel.id).pins(message).delete();
|
||||
await this.client.rest.delete(Routes.channelPin(this.channel.id, message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,8 +195,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();
|
||||
await this.client.rest.put(Routes.channelMessageOwnReaction(this.channel.id, message, emojiId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +207,7 @@ class MessageManager extends CachedManager {
|
||||
message = this.resolveId(message);
|
||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||
|
||||
await this.client.api.channels(this.channel.id).messages(message).delete();
|
||||
await this.client.rest.delete(Routes.channelMessage(this.channel.id, message));
|
||||
}
|
||||
|
||||
async _fetchId(messageId, cache, force) {
|
||||
@@ -216,12 +216,14 @@ 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.rest.get(Routes.channelMessage(this.channel.id, messageId));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
async _fetchMany(options = {}, cache) {
|
||||
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
|
||||
const data = await this.client.rest.get(Routes.channelMessages(this.channel.id), {
|
||||
query: new URLSearchParams(options),
|
||||
});
|
||||
const messages = new Collection();
|
||||
for (const message of data) messages.set(message.id, this._add(message, cache));
|
||||
return messages;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const process = require('node:process');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { OverwriteType } = require('discord-api-types/v9');
|
||||
const { OverwriteType, Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const PermissionOverwrites = require('../structures/PermissionOverwrites');
|
||||
@@ -99,13 +99,10 @@ class PermissionOverwriteManager extends CachedManager {
|
||||
|
||||
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options, existing);
|
||||
|
||||
await this.client.api
|
||||
.channels(this.channel.id)
|
||||
.permissions(userOrRoleId)
|
||||
.put({
|
||||
data: { id: userOrRoleId, type, allow, deny },
|
||||
reason,
|
||||
});
|
||||
await this.client.rest.put(Routes.channelPermission(this.channel.id, userOrRoleId), {
|
||||
body: { id: userOrRoleId, type, allow, deny },
|
||||
reason,
|
||||
});
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
@@ -157,7 +154,7 @@ class PermissionOverwriteManager extends CachedManager {
|
||||
const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole);
|
||||
if (!userOrRoleId) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
|
||||
|
||||
await this.client.api.channels(this.channel.id).permissions(userOrRoleId).delete({ reason });
|
||||
await this.client.rest.delete(Routes.channelPermission(this.channel.id, userOrRoleId), { reason });
|
||||
return this.channel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const MessageReaction = require('../structures/MessageReaction');
|
||||
|
||||
@@ -58,7 +59,7 @@ class ReactionManager extends CachedManager {
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async removeAll() {
|
||||
await this.client.api.channels(this.message.channelId).messages(this.message.id).reactions.delete();
|
||||
await this.client.rest.delete(Routes.channelMessageAllReactions(this.message.channelId, this.message.id));
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -40,9 +41,14 @@ class ReactionUserManager extends CachedManager {
|
||||
*/
|
||||
async fetch({ limit = 100, after } = {}) {
|
||||
const message = this.reaction.message;
|
||||
const data = await this.client.api.channels[message.channelId].messages[message.id].reactions[
|
||||
this.reaction.emoji.identifier
|
||||
].get({ query: { limit, after } });
|
||||
const query = new URLSearchParams({ limit });
|
||||
if (after) {
|
||||
query.set('after', after);
|
||||
}
|
||||
const data = await this.client.rest.get(
|
||||
Routes.channelMessageReaction(message.channelId, message.id, this.reaction.emoji.identifier),
|
||||
{ query },
|
||||
);
|
||||
const users = new Collection();
|
||||
for (const rawUser of data) {
|
||||
const user = this.client.users._add(rawUser);
|
||||
@@ -61,9 +67,11 @@ 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();
|
||||
const route =
|
||||
userId === this.client.user.id
|
||||
? Routes.channelMessageOwnReaction(message.channelId, message.id, this.reaction.emoji.identifier)
|
||||
: Routes.channelMessageUserReaction(message.channelId, message.id, this.reaction.emoji.identifier, userId);
|
||||
await this.client.rest.delete(route);
|
||||
return this.reaction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
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');
|
||||
@@ -66,7 +67,7 @@ class RoleManager extends CachedManager {
|
||||
}
|
||||
|
||||
// We cannot fetch a single role, as of this commit's date, Discord API throws with 405
|
||||
const data = await this.client.api.guilds(this.guild.id).roles.get();
|
||||
const data = await this.client.rest.get(Routes.guildRoles(this.guild.id));
|
||||
const roles = new Collection();
|
||||
for (const role of data) roles.set(role.id, this._add(role, cache));
|
||||
return id ? roles.get(id) ?? null : roles;
|
||||
@@ -143,8 +144,8 @@ class RoleManager extends CachedManager {
|
||||
if (typeof icon !== 'string') icon = undefined;
|
||||
}
|
||||
|
||||
const data = await this.client.api.guilds(this.guild.id).roles.post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
|
||||
body: {
|
||||
name,
|
||||
color,
|
||||
hoist,
|
||||
@@ -190,7 +191,7 @@ class RoleManager extends CachedManager {
|
||||
if (typeof icon !== 'string') icon = undefined;
|
||||
}
|
||||
|
||||
const _data = {
|
||||
const body = {
|
||||
name: data.name,
|
||||
color: typeof data.color === 'undefined' ? undefined : resolveColor(data.color),
|
||||
hoist: data.hoist,
|
||||
@@ -200,7 +201,7 @@ class RoleManager extends CachedManager {
|
||||
unicode_emoji: data.unicodeEmoji,
|
||||
};
|
||||
|
||||
const d = await this.client.api.guilds(this.guild.id).roles(role.id).patch({ data: _data, reason });
|
||||
const d = await this.client.rest.patch(Routes.guildRole(this.guild.id, role.id), { body, reason });
|
||||
|
||||
const clone = role._clone();
|
||||
clone._patch(d);
|
||||
@@ -220,7 +221,7 @@ class RoleManager extends CachedManager {
|
||||
*/
|
||||
async delete(role, reason) {
|
||||
const id = this.resolveId(role);
|
||||
await this.client.api.guilds[this.guild.id].roles[id].delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildRole(this.guild.id, id), { reason });
|
||||
this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: id });
|
||||
}
|
||||
|
||||
@@ -248,9 +249,7 @@ class RoleManager extends CachedManager {
|
||||
}));
|
||||
|
||||
// Call the API to update role positions
|
||||
await this.client.api.guilds(this.guild.id).roles.patch({
|
||||
data: rolePositions,
|
||||
});
|
||||
await this.client.rest.patch(Routes.guildRoles(this.guild.id), { body: rolePositions });
|
||||
return this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: this.guild.id,
|
||||
roles: rolePositions,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { TypeError, Error } = require('../errors');
|
||||
const { StageInstance } = require('../structures/StageInstance');
|
||||
@@ -59,8 +60,8 @@ class StageInstanceManager extends CachedManager {
|
||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||
let { topic, privacyLevel } = options;
|
||||
|
||||
const data = await this.client.api['stage-instances'].post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.stageInstances(), {
|
||||
body: {
|
||||
channel_id: channelId,
|
||||
topic,
|
||||
privacy_level: privacyLevel,
|
||||
@@ -90,7 +91,7 @@ class StageInstanceManager extends CachedManager {
|
||||
if (existing) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api('stage-instances', channelId).get();
|
||||
const data = await this.client.rest.get(Routes.stageInstance(channelId));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
@@ -119,8 +120,8 @@ class StageInstanceManager extends CachedManager {
|
||||
|
||||
let { topic, privacyLevel } = options;
|
||||
|
||||
const data = await this.client.api('stage-instances', channelId).patch({
|
||||
data: {
|
||||
const data = await this.client.rest.patch(Routes.stageInstance(channelId), {
|
||||
body: {
|
||||
topic,
|
||||
privacy_level: privacyLevel,
|
||||
},
|
||||
@@ -144,7 +145,7 @@ class StageInstanceManager extends CachedManager {
|
||||
const channelId = this.guild.channels.resolveId(channel);
|
||||
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
||||
|
||||
await this.client.api('stage-instances', channelId).delete();
|
||||
await this.client.rest.delete(Routes.stageInstance(channelId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { TypeError } = require('../errors');
|
||||
const ThreadChannel = require('../structures/ThreadChannel');
|
||||
@@ -108,16 +108,15 @@ 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;
|
||||
if (startMessage) {
|
||||
const startMessageId = this.channel.messages.resolveId(startMessage);
|
||||
startMessageId = this.channel.messages.resolveId(startMessage);
|
||||
if (!startMessageId) throw new TypeError('INVALID_TYPE', 'startMessage', 'MessageResolvable');
|
||||
path = path.messages(startMessageId);
|
||||
} else if (this.channel.type !== ChannelType.GuildNews) {
|
||||
resolvedType = type ?? resolvedType;
|
||||
}
|
||||
@@ -130,8 +129,8 @@ class ThreadManager extends CachedManager {
|
||||
}
|
||||
}
|
||||
|
||||
const data = await path.threads.post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.threads(this.channel.id, startMessageId), {
|
||||
body: {
|
||||
name,
|
||||
auto_archive_duration: autoArchiveDuration,
|
||||
type: resolvedType,
|
||||
@@ -207,27 +206,37 @@ class ThreadManager extends CachedManager {
|
||||
* @returns {Promise<FetchedThreads>}
|
||||
*/
|
||||
async fetchArchived({ type = 'public', fetchAll = false, before, limit } = {}, cache = true) {
|
||||
let path = this.client.api.channels(this.channel.id);
|
||||
let path = Routes.channelThreads(this.channel.id, type);
|
||||
if (type === 'private' && !fetchAll) {
|
||||
path = path.users('@me');
|
||||
path = Routes.channelJoinedArchivedThreads(this.channel.id);
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
const raw = await path.threads
|
||||
.archived(type)
|
||||
.get({ query: { before: type === 'private' && !fetchAll ? id : timestamp, limit } });
|
||||
|
||||
if (limit) {
|
||||
query.set('limit', limit);
|
||||
}
|
||||
const raw = await this.client.rest.get(path, { query });
|
||||
return this.constructor._mapThreads(raw, this.client, { parent: this.channel, cache });
|
||||
}
|
||||
|
||||
@@ -237,7 +246,7 @@ class ThreadManager extends CachedManager {
|
||||
* @returns {Promise<FetchedThreads>}
|
||||
*/
|
||||
async fetchActive(cache = true) {
|
||||
const raw = await this.client.api.guilds(this.channel.guild.id).threads.active.get();
|
||||
const raw = await this.client.rest.get(Routes.guildActiveThreads(this.channel.guild.id));
|
||||
return this.constructor._mapThreads(raw, this.client, { parent: this.channel, cache });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'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');
|
||||
@@ -77,7 +78,7 @@ class ThreadMemberManager extends CachedManager {
|
||||
async add(member, reason) {
|
||||
const id = member === '@me' ? member : this.client.users.resolveId(member);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'member', 'UserResolvable');
|
||||
await this.client.api.channels(this.thread.id, 'thread-members', id).put({ reason });
|
||||
await this.client.rest.put(Routes.threadMembers(this.thread.id, id), { reason });
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ class ThreadMemberManager extends CachedManager {
|
||||
* @returns {Promise<Snowflake>}
|
||||
*/
|
||||
async remove(id, reason) {
|
||||
await this.client.api.channels(this.thread.id, 'thread-members', id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.threadMembers(this.thread.id, id), { reason });
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -98,12 +99,12 @@ class ThreadMemberManager extends CachedManager {
|
||||
if (existing) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.channels(this.thread.id, 'thread-members', memberId).get();
|
||||
const data = await this.client.rest.get(Routes.threadMembers(this.thread.id, memberId));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
async _fetchMany(cache) {
|
||||
const raw = await this.client.api.channels(this.thread.id, 'thread-members').get();
|
||||
const raw = await this.client.rest.get(Routes.threadMembers(this.thread.id));
|
||||
return raw.reduce((col, member) => col.set(member.user_id, this._add(member, cache)), new Collection());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { GuildMember } = require('../structures/GuildMember');
|
||||
const { Message } = require('../structures/Message');
|
||||
@@ -56,11 +56,7 @@ class UserManager extends CachedManager {
|
||||
if (dmChannel && !dmChannel.partial) return dmChannel;
|
||||
}
|
||||
|
||||
const data = await this.client.api.users(this.client.user.id).channels.post({
|
||||
data: {
|
||||
recipient_id: id,
|
||||
},
|
||||
});
|
||||
const data = await this.client.rest.post(Routes.userChannels(), { body: { recipient_id: id } });
|
||||
return this.client.channels._add(data, null, { cache });
|
||||
}
|
||||
|
||||
@@ -73,7 +69,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.api.channels(dmChannel.id).delete();
|
||||
await this.client.rest.delete(Routes.channel(dmChannel.id));
|
||||
this.client.channels._remove(dmChannel.id);
|
||||
return dmChannel;
|
||||
}
|
||||
@@ -91,7 +87,7 @@ class UserManager extends CachedManager {
|
||||
if (existing && !existing.partial) return existing;
|
||||
}
|
||||
|
||||
const data = await this.client.api.users(id).get();
|
||||
const data = await this.client.rest.get(Routes.user(id));
|
||||
return this._add(data, cache);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const https = require('node:https');
|
||||
const { setTimeout, clearTimeout } = require('node:timers');
|
||||
const FormData = require('form-data');
|
||||
const fetch = require('node-fetch');
|
||||
const { UserAgent } = require('../util/Constants');
|
||||
|
||||
let agent = null;
|
||||
|
||||
class APIRequest {
|
||||
constructor(rest, method, path, options) {
|
||||
this.rest = rest;
|
||||
this.client = rest.client;
|
||||
this.method = method;
|
||||
this.route = options.route;
|
||||
this.options = options;
|
||||
this.retries = 0;
|
||||
|
||||
const { userAgentSuffix } = this.client.options;
|
||||
this.fullUserAgent = `${UserAgent}${userAgentSuffix.length ? `, ${userAgentSuffix.join(', ')}` : ''}`;
|
||||
|
||||
let queryString = '';
|
||||
if (options.query) {
|
||||
const query = Object.entries(options.query)
|
||||
.filter(([, value]) => value !== null && typeof value !== 'undefined')
|
||||
.flatMap(([key, value]) => (Array.isArray(value) ? value.map(v => [key, v]) : [[key, value]]));
|
||||
queryString = new URLSearchParams(query).toString();
|
||||
}
|
||||
this.path = `${path}${queryString && `?${queryString}`}`;
|
||||
}
|
||||
|
||||
make() {
|
||||
agent ??= new https.Agent({ ...this.client.options.http.agent, keepAlive: true });
|
||||
|
||||
const API =
|
||||
this.options.versioned === false
|
||||
? this.client.options.http.api
|
||||
: `${this.client.options.http.api}/v${this.client.options.http.version}`;
|
||||
const url = API + this.path;
|
||||
|
||||
let headers = {
|
||||
...this.client.options.http.headers,
|
||||
'User-Agent': this.fullUserAgent,
|
||||
};
|
||||
|
||||
if (this.options.auth !== false) headers.Authorization = this.rest.getAuth();
|
||||
if (this.options.reason) headers['X-Audit-Log-Reason'] = encodeURIComponent(this.options.reason);
|
||||
if (this.options.headers) headers = Object.assign(headers, this.options.headers);
|
||||
|
||||
let body;
|
||||
if (this.options.files?.length) {
|
||||
body = new FormData();
|
||||
for (const [index, file] of this.options.files.entries()) {
|
||||
if (file?.file) body.append(file.key ?? `files[${index}]`, file.file, file.name);
|
||||
}
|
||||
if (typeof this.options.data !== 'undefined') {
|
||||
if (this.options.dontUsePayloadJSON) {
|
||||
for (const [key, value] of Object.entries(this.options.data)) body.append(key, value);
|
||||
} else {
|
||||
body.append('payload_json', JSON.stringify(this.options.data));
|
||||
}
|
||||
}
|
||||
headers = Object.assign(headers, body.getHeaders());
|
||||
// eslint-disable-next-line eqeqeq
|
||||
} else if (this.options.data != null) {
|
||||
body = JSON.stringify(this.options.data);
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), this.client.options.restRequestTimeout).unref();
|
||||
return fetch(url, {
|
||||
method: this.method,
|
||||
headers,
|
||||
agent,
|
||||
body,
|
||||
signal: controller.signal,
|
||||
}).finally(() => clearTimeout(timeout));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIRequest;
|
||||
@@ -1,53 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const noop = () => {}; // eslint-disable-line no-empty-function
|
||||
const methods = ['get', 'post', 'delete', 'patch', 'put'];
|
||||
const reflectors = [
|
||||
'toString',
|
||||
'valueOf',
|
||||
'inspect',
|
||||
'constructor',
|
||||
Symbol.toPrimitive,
|
||||
Symbol.for('nodejs.util.inspect.custom'),
|
||||
];
|
||||
|
||||
function buildRoute(manager) {
|
||||
const route = [''];
|
||||
const handler = {
|
||||
get(target, name) {
|
||||
if (reflectors.includes(name)) return () => route.join('/');
|
||||
if (methods.includes(name)) {
|
||||
const routeBucket = [];
|
||||
for (let i = 0; i < route.length; i++) {
|
||||
// Reactions routes and sub-routes all share the same bucket
|
||||
if (route[i - 1] === 'reactions') break;
|
||||
// Literal ids should only be taken account if they are the Major id (the Channel/Guild id)
|
||||
if (/\d{16,19}/g.test(route[i]) && !/channels|guilds/.test(route[i - 1])) routeBucket.push(':id');
|
||||
// All other parts of the route should be considered as part of the bucket identifier
|
||||
else routeBucket.push(route[i]);
|
||||
}
|
||||
return options =>
|
||||
manager.request(
|
||||
name,
|
||||
route.join('/'),
|
||||
Object.assign(
|
||||
{
|
||||
versioned: manager.versioned,
|
||||
route: routeBucket.join('/'),
|
||||
},
|
||||
options,
|
||||
),
|
||||
);
|
||||
}
|
||||
route.push(name);
|
||||
return new Proxy(noop, handler);
|
||||
},
|
||||
apply(target, _, args) {
|
||||
route.push(...args.filter(x => x != null)); // eslint-disable-line eqeqeq
|
||||
return new Proxy(noop, handler);
|
||||
},
|
||||
};
|
||||
return new Proxy(noop, handler);
|
||||
}
|
||||
|
||||
module.exports = buildRoute;
|
||||
@@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents an error from the Discord API.
|
||||
* @extends Error
|
||||
*/
|
||||
class DiscordAPIError extends Error {
|
||||
constructor(error, status, request) {
|
||||
super();
|
||||
const flattened = this.constructor.flattenErrors(error.errors ?? error).join('\n');
|
||||
this.name = 'DiscordAPIError';
|
||||
this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message ?? flattened;
|
||||
|
||||
/**
|
||||
* The HTTP method used for the request
|
||||
* @type {string}
|
||||
*/
|
||||
this.method = request.method;
|
||||
|
||||
/**
|
||||
* The path of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.path = request.path;
|
||||
|
||||
/**
|
||||
* HTTP error code returned by Discord
|
||||
* @type {number}
|
||||
*/
|
||||
this.code = error.code;
|
||||
|
||||
/**
|
||||
* The HTTP status code
|
||||
* @type {number}
|
||||
*/
|
||||
this.httpStatus = status;
|
||||
|
||||
/**
|
||||
* The data associated with the request that caused this error
|
||||
* @type {HTTPErrorData}
|
||||
*/
|
||||
this.requestData = {
|
||||
json: request.options.data,
|
||||
files: request.options.files ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an errors object returned from the API into an array.
|
||||
* @param {APIError} obj Discord errors object
|
||||
* @param {string} [key] Used internally to determine key names of nested fields
|
||||
* @returns {string[]}
|
||||
* @private
|
||||
*/
|
||||
static flattenErrors(obj, key = '') {
|
||||
let messages = [];
|
||||
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (k === 'message') continue;
|
||||
const newKey = key ? (isNaN(k) ? `${key}.${k}` : `${key}[${k}]`) : k;
|
||||
|
||||
if (v._errors) {
|
||||
messages.push(`${newKey}: ${v._errors.map(e => e.message).join(' ')}`);
|
||||
} else if (v.code ?? v.message) {
|
||||
messages.push(`${v.code ? `${v.code}: ` : ''}${v.message}`.trim());
|
||||
} else if (typeof v === 'string') {
|
||||
messages.push(v);
|
||||
} else {
|
||||
messages = messages.concat(this.flattenErrors(v, newKey));
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DiscordAPIError;
|
||||
|
||||
/**
|
||||
* @external APIError
|
||||
* @see {@link https://discord.com/developers/docs/reference#error-messages}
|
||||
*/
|
||||
@@ -1,61 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents an HTTP error from a request.
|
||||
* @extends Error
|
||||
*/
|
||||
class HTTPError extends Error {
|
||||
constructor(message, name, code, request) {
|
||||
super(message);
|
||||
|
||||
/**
|
||||
* The name of the error
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = name;
|
||||
|
||||
/**
|
||||
* HTTP error code returned from the request
|
||||
* @type {number}
|
||||
*/
|
||||
this.code = code ?? 500;
|
||||
|
||||
/**
|
||||
* The HTTP method used for the request
|
||||
* @type {string}
|
||||
*/
|
||||
this.method = request.method;
|
||||
|
||||
/**
|
||||
* The path of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.path = request.path;
|
||||
|
||||
/**
|
||||
* The HTTP data that was sent to Discord
|
||||
* @typedef {Object} HTTPErrorData
|
||||
* @property {*} json The JSON data that was sent
|
||||
* @property {HTTPAttachmentData[]} files The files that were sent with this request, if any
|
||||
*/
|
||||
|
||||
/**
|
||||
* The attachment data that is sent to Discord
|
||||
* @typedef {Object} HTTPAttachmentData
|
||||
* @property {string|Buffer|Stream} attachment The source of this attachment data
|
||||
* @property {string} name The file name
|
||||
* @property {Buffer|Stream} file The file buffer
|
||||
*/
|
||||
|
||||
/**
|
||||
* The data associated with the request that caused this error
|
||||
* @type {HTTPErrorData}
|
||||
*/
|
||||
this.requestData = {
|
||||
json: request.options.data,
|
||||
files: request.options.files ?? [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HTTPError;
|
||||
@@ -1,62 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { setInterval } = require('node:timers');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const APIRequest = require('./APIRequest');
|
||||
const routeBuilder = require('./APIRouter');
|
||||
const RequestHandler = require('./RequestHandler');
|
||||
const { Error } = require('../errors');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
|
||||
class RESTManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
this.handlers = new Collection();
|
||||
this.versioned = true;
|
||||
this.globalLimit = client.options.restGlobalRateLimit > 0 ? client.options.restGlobalRateLimit : Infinity;
|
||||
this.globalRemaining = this.globalLimit;
|
||||
this.globalReset = null;
|
||||
this.globalDelay = null;
|
||||
if (client.options.restSweepInterval > 0) {
|
||||
this.sweepInterval = setInterval(() => {
|
||||
this.handlers.sweep(handler => handler._inactive);
|
||||
}, client.options.restSweepInterval * 1_000).unref();
|
||||
}
|
||||
}
|
||||
|
||||
get api() {
|
||||
return routeBuilder(this);
|
||||
}
|
||||
|
||||
getAuth() {
|
||||
const token = this.client.token ?? this.client.accessToken;
|
||||
if (token) return `Bot ${token}`;
|
||||
throw new Error('TOKEN_MISSING');
|
||||
}
|
||||
|
||||
get cdn() {
|
||||
return Endpoints.CDN(this.client.options.http.cdn);
|
||||
}
|
||||
|
||||
request(method, url, options = {}) {
|
||||
const apiRequest = new APIRequest(this, method, url, options);
|
||||
let handler = this.handlers.get(apiRequest.route);
|
||||
|
||||
if (!handler) {
|
||||
handler = new RequestHandler(this);
|
||||
this.handlers.set(apiRequest.route, handler);
|
||||
}
|
||||
|
||||
return handler.push(apiRequest);
|
||||
}
|
||||
|
||||
get endpoint() {
|
||||
return this.client.options.http.api;
|
||||
}
|
||||
|
||||
set endpoint(endpoint) {
|
||||
this.client.options.http.api = endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTManager;
|
||||
@@ -1,55 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Represents a RateLimit error from a request.
|
||||
* @extends Error
|
||||
*/
|
||||
class RateLimitError extends Error {
|
||||
constructor({ timeout, limit, method, path, route, global }) {
|
||||
super(`A ${global ? 'global ' : ''}rate limit was hit on route ${route}`);
|
||||
|
||||
/**
|
||||
* The name of the error
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = 'RateLimitError';
|
||||
|
||||
/**
|
||||
* Time until this rate limit ends, in milliseconds
|
||||
* @type {number}
|
||||
*/
|
||||
this.timeout = timeout;
|
||||
|
||||
/**
|
||||
* The HTTP method used for the request
|
||||
* @type {string}
|
||||
*/
|
||||
this.method = method;
|
||||
|
||||
/**
|
||||
* The path of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.path = path;
|
||||
|
||||
/**
|
||||
* The route of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.route = route;
|
||||
|
||||
/**
|
||||
* Whether this rate limit is global
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.global = global;
|
||||
|
||||
/**
|
||||
* The maximum amount of requests of this endpoint
|
||||
* @type {number}
|
||||
*/
|
||||
this.limit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RateLimitError;
|
||||
@@ -1,379 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { setTimeout } = require('node:timers');
|
||||
const { setTimeout: sleep } = require('node:timers/promises');
|
||||
const { AsyncQueue } = require('@sapphire/async-queue');
|
||||
const DiscordAPIError = require('./DiscordAPIError');
|
||||
const HTTPError = require('./HTTPError');
|
||||
const RateLimitError = require('./RateLimitError');
|
||||
const {
|
||||
Events: { DEBUG, RATE_LIMIT, INVALID_REQUEST_WARNING, API_RESPONSE, API_REQUEST },
|
||||
} = require('../util/Constants');
|
||||
|
||||
function parseResponse(res) {
|
||||
if (res.headers.get('content-type').startsWith('application/json')) return res.json();
|
||||
return res.buffer();
|
||||
}
|
||||
|
||||
function getAPIOffset(serverDate) {
|
||||
return Date.parse(serverDate) - Date.now();
|
||||
}
|
||||
|
||||
function calculateReset(reset, resetAfter, serverDate) {
|
||||
// Use direct reset time when available, server date becomes irrelevant in this case
|
||||
if (resetAfter) {
|
||||
return Date.now() + Number(resetAfter) * 1_000;
|
||||
}
|
||||
return Number(reset) * 1_000 - getAPIOffset(serverDate);
|
||||
}
|
||||
|
||||
/* Invalid request limiting is done on a per-IP basis, not a per-token basis.
|
||||
* The best we can do is track invalid counts process-wide (on the theory that
|
||||
* users could have multiple bots run from one process) rather than per-bot.
|
||||
* Therefore, store these at file scope here rather than in the client's
|
||||
* RESTManager object.
|
||||
*/
|
||||
let invalidCount = 0;
|
||||
let invalidCountResetTime = null;
|
||||
|
||||
class RequestHandler {
|
||||
constructor(manager) {
|
||||
this.manager = manager;
|
||||
this.queue = new AsyncQueue();
|
||||
this.reset = -1;
|
||||
this.remaining = -1;
|
||||
this.limit = -1;
|
||||
}
|
||||
|
||||
async push(request) {
|
||||
await this.queue.wait();
|
||||
try {
|
||||
return await this.execute(request);
|
||||
} finally {
|
||||
this.queue.shift();
|
||||
}
|
||||
}
|
||||
|
||||
get globalLimited() {
|
||||
return this.manager.globalRemaining <= 0 && Date.now() < this.manager.globalReset;
|
||||
}
|
||||
|
||||
get localLimited() {
|
||||
return this.remaining <= 0 && Date.now() < this.reset;
|
||||
}
|
||||
|
||||
get limited() {
|
||||
return this.globalLimited || this.localLimited;
|
||||
}
|
||||
|
||||
get _inactive() {
|
||||
return this.queue.remaining === 0 && !this.limited;
|
||||
}
|
||||
|
||||
globalDelayFor(ms) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
this.manager.globalDelay = null;
|
||||
resolve();
|
||||
}, ms).unref();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the request should be queued or whether a RateLimitError should be thrown
|
||||
*/
|
||||
async onRateLimit(request, limit, timeout, isGlobal) {
|
||||
const { options } = this.manager.client;
|
||||
if (!options.rejectOnRateLimit) return;
|
||||
|
||||
const rateLimitData = {
|
||||
timeout,
|
||||
limit,
|
||||
method: request.method,
|
||||
path: request.path,
|
||||
route: request.route,
|
||||
global: isGlobal,
|
||||
};
|
||||
const shouldThrow =
|
||||
typeof options.rejectOnRateLimit === 'function'
|
||||
? await options.rejectOnRateLimit(rateLimitData)
|
||||
: options.rejectOnRateLimit.some(route => rateLimitData.route.startsWith(route.toLowerCase()));
|
||||
if (shouldThrow) {
|
||||
throw new RateLimitError(rateLimitData);
|
||||
}
|
||||
}
|
||||
|
||||
async execute(request) {
|
||||
/*
|
||||
* After calculations have been done, pre-emptively stop further requests
|
||||
* Potentially loop until this task can run if e.g. the global rate limit is hit twice
|
||||
*/
|
||||
while (this.limited) {
|
||||
const isGlobal = this.globalLimited;
|
||||
let limit, timeout, delayPromise;
|
||||
|
||||
if (isGlobal) {
|
||||
// Set the variables based on the global rate limit
|
||||
limit = this.manager.globalLimit;
|
||||
timeout = this.manager.globalReset + this.manager.client.options.restTimeOffset - Date.now();
|
||||
} else {
|
||||
// Set the variables based on the route-specific rate limit
|
||||
limit = this.limit;
|
||||
timeout = this.reset + this.manager.client.options.restTimeOffset - Date.now();
|
||||
}
|
||||
|
||||
if (this.manager.client.listenerCount(RATE_LIMIT)) {
|
||||
/**
|
||||
* Emitted when the client hits a rate limit while making a request
|
||||
* @event BaseClient#rateLimit
|
||||
* @param {RateLimitData} rateLimitData Object containing the rate limit info
|
||||
*/
|
||||
this.manager.client.emit(RATE_LIMIT, {
|
||||
timeout,
|
||||
limit,
|
||||
method: request.method,
|
||||
path: request.path,
|
||||
route: request.route,
|
||||
global: isGlobal,
|
||||
});
|
||||
}
|
||||
|
||||
if (isGlobal) {
|
||||
// If this is the first task to reach the global timeout, set the global delay
|
||||
if (!this.manager.globalDelay) {
|
||||
// The global delay function should clear the global delay state when it is resolved
|
||||
this.manager.globalDelay = this.globalDelayFor(timeout);
|
||||
}
|
||||
delayPromise = this.manager.globalDelay;
|
||||
} else {
|
||||
delayPromise = sleep(timeout);
|
||||
}
|
||||
|
||||
// Determine whether a RateLimitError should be thrown
|
||||
await this.onRateLimit(request, limit, timeout, isGlobal); // eslint-disable-line no-await-in-loop
|
||||
|
||||
// Wait for the timeout to expire in order to avoid an actual 429
|
||||
await delayPromise; // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
|
||||
// As the request goes out, update the global usage information
|
||||
if (!this.manager.globalReset || this.manager.globalReset < Date.now()) {
|
||||
this.manager.globalReset = Date.now() + 1_000;
|
||||
this.manager.globalRemaining = this.manager.globalLimit;
|
||||
}
|
||||
this.manager.globalRemaining--;
|
||||
|
||||
/**
|
||||
* Represents a request that will or has been made to the Discord API
|
||||
* @typedef {Object} APIRequest
|
||||
* @property {HTTPMethod} method The HTTP method used in this request
|
||||
* @property {string} path The full path used to make the request
|
||||
* @property {string} route The API route identifying the rate limit for this request
|
||||
* @property {Object} options Additional options for this request
|
||||
* @property {number} retries The number of times this request has been attempted
|
||||
*/
|
||||
|
||||
if (this.manager.client.listenerCount(API_REQUEST)) {
|
||||
/**
|
||||
* Emitted before every API request.
|
||||
* This event can emit several times for the same request, e.g. when hitting a rate limit.
|
||||
* <info>This is an informational event that is emitted quite frequently,
|
||||
* it is highly recommended to check `request.path` to filter the data.</info>
|
||||
* @event BaseClient#apiRequest
|
||||
* @param {APIRequest} request The request that is about to be sent
|
||||
*/
|
||||
this.manager.client.emit(API_REQUEST, {
|
||||
method: request.method,
|
||||
path: request.path,
|
||||
route: request.route,
|
||||
options: request.options,
|
||||
retries: request.retries,
|
||||
});
|
||||
}
|
||||
|
||||
// Perform the request
|
||||
let res;
|
||||
try {
|
||||
res = await request.make();
|
||||
} catch (error) {
|
||||
// Retry the specified number of times for request abortions
|
||||
if (request.retries === this.manager.client.options.retryLimit) {
|
||||
throw new HTTPError(error.message, error.constructor.name, error.status, request);
|
||||
}
|
||||
|
||||
request.retries++;
|
||||
return this.execute(request);
|
||||
}
|
||||
|
||||
if (this.manager.client.listenerCount(API_RESPONSE)) {
|
||||
/**
|
||||
* Emitted after every API request has received a response.
|
||||
* This event does not necessarily correlate to completion of the request, e.g. when hitting a rate limit.
|
||||
* <info>This is an informational event that is emitted quite frequently,
|
||||
* it is highly recommended to check `request.path` to filter the data.</info>
|
||||
* @event BaseClient#apiResponse
|
||||
* @param {APIRequest} request The request that triggered this response
|
||||
* @param {Response} response The response received from the Discord API
|
||||
*/
|
||||
this.manager.client.emit(
|
||||
API_RESPONSE,
|
||||
{
|
||||
method: request.method,
|
||||
path: request.path,
|
||||
route: request.route,
|
||||
options: request.options,
|
||||
retries: request.retries,
|
||||
},
|
||||
res.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let sublimitTimeout;
|
||||
if (res.headers) {
|
||||
const serverDate = res.headers.get('date');
|
||||
const limit = res.headers.get('x-ratelimit-limit');
|
||||
const remaining = res.headers.get('x-ratelimit-remaining');
|
||||
const reset = res.headers.get('x-ratelimit-reset');
|
||||
const resetAfter = res.headers.get('x-ratelimit-reset-after');
|
||||
this.limit = limit ? Number(limit) : Infinity;
|
||||
this.remaining = remaining ? Number(remaining) : 1;
|
||||
|
||||
this.reset = reset || resetAfter ? calculateReset(reset, resetAfter, serverDate) : Date.now();
|
||||
|
||||
// https://github.com/discord/discord-api-docs/issues/182
|
||||
if (!resetAfter && request.route.includes('reactions')) {
|
||||
this.reset = Date.parse(serverDate) - getAPIOffset(serverDate) + 250;
|
||||
}
|
||||
|
||||
// Handle retryAfter, which means we have actually hit a rate limit
|
||||
let retryAfter = res.headers.get('retry-after');
|
||||
retryAfter = retryAfter ? Number(retryAfter) * 1_000 : -1;
|
||||
if (retryAfter > 0) {
|
||||
// If the global rate limit header is set, that means we hit the global rate limit
|
||||
if (res.headers.get('x-ratelimit-global')) {
|
||||
this.manager.globalRemaining = 0;
|
||||
this.manager.globalReset = Date.now() + retryAfter;
|
||||
} else if (!this.localLimited) {
|
||||
/*
|
||||
* This is a sublimit (e.g. 2 channel name changes/10 minutes) since the headers don't indicate a
|
||||
* route-wide rate limit. Don't update remaining or reset to avoid rate limiting the whole
|
||||
* endpoint, just set a reset time on the request itself to avoid retrying too soon.
|
||||
*/
|
||||
sublimitTimeout = retryAfter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count the invalid requests
|
||||
if (res.status === 401 || res.status === 403 || res.status === 429) {
|
||||
if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
|
||||
invalidCountResetTime = Date.now() + 1_000 * 60 * 10;
|
||||
invalidCount = 0;
|
||||
}
|
||||
invalidCount++;
|
||||
|
||||
const emitInvalid =
|
||||
this.manager.client.listenerCount(INVALID_REQUEST_WARNING) &&
|
||||
this.manager.client.options.invalidRequestWarningInterval > 0 &&
|
||||
invalidCount % this.manager.client.options.invalidRequestWarningInterval === 0;
|
||||
if (emitInvalid) {
|
||||
/**
|
||||
* @typedef {Object} InvalidRequestWarningData
|
||||
* @property {number} count Number of invalid requests that have been made in the window
|
||||
* @property {number} remainingTime Time in milliseconds remaining before the count resets
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted periodically when the process sends invalid requests to let users avoid the
|
||||
* 10k invalid requests in 10 minutes threshold that causes a ban
|
||||
* @event BaseClient#invalidRequestWarning
|
||||
* @param {InvalidRequestWarningData} invalidRequestWarningData Object containing the invalid request info
|
||||
*/
|
||||
this.manager.client.emit(INVALID_REQUEST_WARNING, {
|
||||
count: invalidCount,
|
||||
remainingTime: invalidCountResetTime - Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 2xx and 3xx responses
|
||||
if (res.ok) {
|
||||
// Nothing wrong with the request, proceed with the next one
|
||||
return parseResponse(res);
|
||||
}
|
||||
|
||||
// Handle 4xx responses
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
// Handle ratelimited requests
|
||||
if (res.status === 429) {
|
||||
const isGlobal = this.globalLimited;
|
||||
let limit, timeout;
|
||||
if (isGlobal) {
|
||||
// Set the variables based on the global rate limit
|
||||
limit = this.manager.globalLimit;
|
||||
timeout = this.manager.globalReset + this.manager.client.options.restTimeOffset - Date.now();
|
||||
} else {
|
||||
// Set the variables based on the route-specific rate limit
|
||||
limit = this.limit;
|
||||
timeout = this.reset + this.manager.client.options.restTimeOffset - Date.now();
|
||||
}
|
||||
|
||||
this.manager.client.emit(
|
||||
DEBUG,
|
||||
`Hit a 429 while executing a request.
|
||||
Global : ${isGlobal}
|
||||
Method : ${request.method}
|
||||
Path : ${request.path}
|
||||
Route : ${request.route}
|
||||
Limit : ${limit}
|
||||
Timeout : ${timeout}ms
|
||||
Sublimit: ${sublimitTimeout ? `${sublimitTimeout}ms` : 'None'}`,
|
||||
);
|
||||
|
||||
await this.onRateLimit(request, limit, timeout, isGlobal);
|
||||
|
||||
// If caused by a sublimit, wait it out here so other requests on the route can be handled
|
||||
if (sublimitTimeout) {
|
||||
await sleep(sublimitTimeout);
|
||||
}
|
||||
return this.execute(request);
|
||||
}
|
||||
|
||||
// Handle possible malformed requests
|
||||
let data;
|
||||
try {
|
||||
data = await parseResponse(res);
|
||||
} catch (err) {
|
||||
throw new HTTPError(err.message, err.constructor.name, err.status, request);
|
||||
}
|
||||
|
||||
throw new DiscordAPIError(data, res.status, request);
|
||||
}
|
||||
|
||||
// Handle 5xx responses
|
||||
if (res.status >= 500 && res.status < 600) {
|
||||
// Retry the specified number of times for possible serverside issues
|
||||
if (request.retries === this.manager.client.options.retryLimit) {
|
||||
throw new HTTPError(res.statusText, res.constructor.name, res.status, request);
|
||||
}
|
||||
|
||||
request.retries++;
|
||||
return this.execute(request);
|
||||
}
|
||||
|
||||
// Fallback in the rare case a status code outside the range 200..=599 is returned
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequestHandler;
|
||||
|
||||
/**
|
||||
* @external HTTPMethod
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external Response
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Response}
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ class AnonymousGuild extends BaseGuild {
|
||||
* @returns {?string}
|
||||
*/
|
||||
bannerURL(options = {}) {
|
||||
return this.banner && this.client.rest.cdn.Banner(this.id, this.banner, options);
|
||||
return this.banner && this.client.rest.cdn.banner(this.id, this.banner, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ class AnonymousGuild extends BaseGuild {
|
||||
* @returns {?string}
|
||||
*/
|
||||
splashURL(options = {}) {
|
||||
return this.splash && this.client.rest.cdn.Splash(this.id, this.splash, options);
|
||||
return this.splash && this.client.rest.cdn.splash(this.id, this.splash, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { InteractionResponseType } = require('discord-api-types/v9');
|
||||
const { InteractionResponseType, Routes } = require('discord-api-types/v9');
|
||||
const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver');
|
||||
const Interaction = require('./Interaction');
|
||||
|
||||
@@ -70,8 +70,8 @@ class AutocompleteInteraction extends Interaction {
|
||||
async respond(options) {
|
||||
if (this.responded) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.ApplicationCommandAutocompleteResult,
|
||||
data: {
|
||||
choices: options,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,7 @@ class BaseGuild extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.Icon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.icon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +100,9 @@ class BaseGuild extends Base {
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
async fetch() {
|
||||
const data = await this.client.api.guilds(this.id).get({ query: { with_counts: true } });
|
||||
const data = await this.client.rest.get(Routes.guild(this.id), {
|
||||
query: new URLSearchParams({ with_counts: true }),
|
||||
});
|
||||
return this.client.guilds._add(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const GuildChannel = require('./GuildChannel');
|
||||
const Webhook = require('./Webhook');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
@@ -122,7 +123,7 @@ class BaseGuildTextChannel extends GuildChannel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchWebhooks() {
|
||||
const data = await this.client.api.channels[this.id].webhooks.get();
|
||||
const data = await this.client.rest.get(Routes.channelWebhooks(this.id));
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
@@ -153,8 +154,8 @@ class BaseGuildTextChannel extends GuildChannel {
|
||||
if (typeof avatar === 'string' && !avatar.startsWith('data:')) {
|
||||
avatar = await DataResolver.resolveImage(avatar);
|
||||
}
|
||||
const data = await this.client.api.channels[this.id].webhooks.post({
|
||||
data: {
|
||||
const data = await this.client.rest.post(Routes.channelWebhooks(this.id), {
|
||||
body: {
|
||||
name,
|
||||
avatar,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { ThreadChannelTypes } = require('../util/Constants');
|
||||
let CategoryChannel;
|
||||
@@ -88,7 +88,7 @@ class Channel extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async delete() {
|
||||
await this.client.api.channels(this.id).delete();
|
||||
await this.client.rest.delete(Routes.channel(this.id));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Team = require('./Team');
|
||||
const Application = require('./interfaces/Application');
|
||||
const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
|
||||
@@ -96,7 +97,7 @@ class ClientApplication extends Application {
|
||||
* @returns {Promise<ClientApplication>}
|
||||
*/
|
||||
async fetch() {
|
||||
const app = await this.client.api.oauth2.applications('@me').get();
|
||||
const app = await this.client.rest.get(Routes.oauth2CurrentApplication());
|
||||
this._patch(app);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const User = require('./User');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
|
||||
@@ -55,8 +56,9 @@ class ClientUser extends User {
|
||||
*/
|
||||
async edit(data) {
|
||||
if (typeof data.avatar !== 'undefined') data.avatar = await DataResolver.resolveImage(data.avatar);
|
||||
const newData = await this.client.api.users('@me').patch({ data });
|
||||
const newData = await this.client.rest.patch(Routes.user(), { body: data });
|
||||
this.client.token = newData.token;
|
||||
this.client.rest.setToken(newData.token);
|
||||
const { updated } = this.client.actions.UserUpdate.handle(newData);
|
||||
return updated ?? this;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Emoji extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get url() {
|
||||
return this.id && this.client.rest.cdn.Emoji(this.id, this.animated ? 'gif' : 'png');
|
||||
return this.id && this.client.rest.cdn.emoji(this.id, this.animated ? 'gif' : 'png');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { GuildPremiumTier, ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, GuildPremiumTier, Routes } = require('discord-api-types/v9');
|
||||
const AnonymousGuild = require('./AnonymousGuild');
|
||||
const GuildAuditLogs = require('./GuildAuditLogs');
|
||||
const GuildPreview = require('./GuildPreview');
|
||||
@@ -9,7 +9,7 @@ const GuildTemplate = require('./GuildTemplate');
|
||||
const Integration = require('./Integration');
|
||||
const Webhook = require('./Webhook');
|
||||
const WelcomeScreen = require('./WelcomeScreen');
|
||||
const { Error } = require('../errors');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
const GuildApplicationCommandManager = require('../managers/GuildApplicationCommandManager');
|
||||
const GuildBanManager = require('../managers/GuildBanManager');
|
||||
const GuildChannelManager = require('../managers/GuildChannelManager');
|
||||
@@ -477,7 +477,7 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {?string}
|
||||
*/
|
||||
discoverySplashURL(options = {}) {
|
||||
return this.discoverySplash && this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, options);
|
||||
return this.discoverySplash && this.client.rest.cdn.discoverySplash(this.id, this.discoverySplash, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -582,7 +582,7 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchIntegrations() {
|
||||
const data = await this.client.api.guilds(this.id).integrations.get();
|
||||
const data = await this.client.rest.get(Routes.guildIntegrations(this.id));
|
||||
return data.reduce(
|
||||
(collection, integration) => collection.set(integration.id, new Integration(this.client, integration, this)),
|
||||
new Collection(),
|
||||
@@ -595,7 +595,7 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {Promise<Collection<string, GuildTemplate>>}
|
||||
*/
|
||||
async fetchTemplates() {
|
||||
const templates = await this.client.api.guilds(this.id).templates.get();
|
||||
const templates = await this.client.rest.get(Routes.guildTemplate(this.id));
|
||||
return templates.reduce((col, data) => col.set(data.code, new GuildTemplate(this.client, data)), new Collection());
|
||||
}
|
||||
|
||||
@@ -604,7 +604,7 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {Promise<WelcomeScreen>}
|
||||
*/
|
||||
async fetchWelcomeScreen() {
|
||||
const data = await this.client.api.guilds(this.id, 'welcome-screen').get();
|
||||
const data = await this.client.rest.get(Routes.guildWelcomeScreen(this.id));
|
||||
return new WelcomeScreen(this, data);
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {Promise<GuildTemplate>}
|
||||
*/
|
||||
async createTemplate(name, description) {
|
||||
const data = await this.client.api.guilds(this.id).templates.post({ data: { name, description } });
|
||||
const data = await this.client.rest.post(Routes.guildTemplates(this.id), { body: { name, description } });
|
||||
return new GuildTemplate(this.client, data);
|
||||
}
|
||||
|
||||
@@ -624,7 +624,7 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {Promise<GuildPreview>}
|
||||
*/
|
||||
async fetchPreview() {
|
||||
const data = await this.client.api.guilds(this.id).preview.get();
|
||||
const data = await this.client.rest.get(Routes.guildPreview(this.id));
|
||||
return new GuildPreview(this.client, data);
|
||||
}
|
||||
|
||||
@@ -651,7 +651,7 @@ class Guild extends AnonymousGuild {
|
||||
if (!this.features.includes('VANITY_URL')) {
|
||||
throw new Error('VANITY_URL');
|
||||
}
|
||||
const data = await this.client.api.guilds(this.id, 'vanity-url').get();
|
||||
const data = await this.client.rest.get(Routes.guildVanityUrl(this.id));
|
||||
this.vanityURLCode = data.code;
|
||||
this.vanityURLUses = data.uses;
|
||||
|
||||
@@ -668,7 +668,7 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchWebhooks() {
|
||||
const apiHooks = await this.client.api.guilds(this.id).webhooks.get();
|
||||
const apiHooks = await this.client.rest.get(Routes.guildWebhooks(this.id));
|
||||
const hooks = new Collection();
|
||||
for (const hook of apiHooks) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
@@ -711,7 +711,7 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchWidgetSettings() {
|
||||
const data = await this.client.api.guilds(this.id).widget.get();
|
||||
const data = await this.client.rest.get(Routes.guildWidgetSettings(this.id));
|
||||
this.widgetEnabled = data.enabled;
|
||||
this.widgetChannelId = data.channel_id;
|
||||
return {
|
||||
@@ -742,14 +742,27 @@ class Guild extends AnonymousGuild {
|
||||
async fetchAuditLogs(options = {}) {
|
||||
if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id;
|
||||
|
||||
const data = await this.client.api.guilds(this.id)['audit-logs'].get({
|
||||
query: {
|
||||
before: options.before,
|
||||
limit: options.limit,
|
||||
user_id: this.client.users.resolveId(options.user),
|
||||
action_type: options.type,
|
||||
},
|
||||
});
|
||||
const query = new URLSearchParams();
|
||||
|
||||
if (options.before) {
|
||||
query.set('before', options.before);
|
||||
}
|
||||
|
||||
if (options.limit) {
|
||||
query.set('limit', options.limit);
|
||||
}
|
||||
|
||||
if (options.user) {
|
||||
const id = this.client.user.resolveId(options.user);
|
||||
if (!id) throw new TypeError('INVALID_TYPE', 'user', 'UserResolvable');
|
||||
query.set('user_id', id);
|
||||
}
|
||||
|
||||
if (options.type) {
|
||||
query.set('action_type', options.type);
|
||||
}
|
||||
|
||||
const data = await this.client.rest.get(Routes.guildAuditLog(this.id), { query });
|
||||
return GuildAuditLogs.build(this, data);
|
||||
}
|
||||
|
||||
@@ -848,7 +861,7 @@ class Guild extends AnonymousGuild {
|
||||
}
|
||||
if (data.preferredLocale) _data.preferred_locale = data.preferredLocale;
|
||||
if ('premiumProgressBarEnabled' in data) _data.premium_progress_bar_enabled = data.premiumProgressBarEnabled;
|
||||
const newData = await this.client.api.guilds(this.id).patch({ data: _data, reason });
|
||||
const newData = await this.client.rest.patch(Routes.guild(this.id), { body: _data, reason });
|
||||
return this.client.actions.GuildUpdate.handle(newData).updated;
|
||||
}
|
||||
|
||||
@@ -912,8 +925,8 @@ class Guild extends AnonymousGuild {
|
||||
};
|
||||
});
|
||||
|
||||
const patchData = await this.client.api.guilds(this.id, 'welcome-screen').patch({
|
||||
data: {
|
||||
const patchData = await this.client.rest.patch(Routes.guildWelcomeScreen(this.id), {
|
||||
body: {
|
||||
welcome_channels,
|
||||
description,
|
||||
enabled,
|
||||
@@ -1166,8 +1179,8 @@ class Guild extends AnonymousGuild {
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
async setWidgetSettings(settings, reason) {
|
||||
await this.client.api.guilds(this.id).widget.patch({
|
||||
data: {
|
||||
await this.client.rest.patch(Routes.guildWidgetSettings(this.id), {
|
||||
body: {
|
||||
enabled: settings.enabled,
|
||||
channel_id: this.channels.resolveId(settings.channel),
|
||||
},
|
||||
@@ -1187,7 +1200,7 @@ class Guild extends AnonymousGuild {
|
||||
*/
|
||||
async leave() {
|
||||
if (this.ownerId === this.client.user.id) throw new Error('GUILD_OWNED');
|
||||
await this.client.api.users('@me').guilds(this.id).delete();
|
||||
await this.client.rest.delete(Routes.userGuild(this.id));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1201,7 +1214,7 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async delete() {
|
||||
await this.client.api.guilds(this.id).delete();
|
||||
await this.client.rest.delete(Routes.guild(this.id));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const { Channel } = require('./Channel');
|
||||
const PermissionOverwrites = require('./PermissionOverwrites');
|
||||
const { Error } = require('../errors');
|
||||
@@ -323,8 +323,8 @@ class GuildChannel extends Channel {
|
||||
}
|
||||
}
|
||||
|
||||
const newData = await this.client.api.channels(this.id).patch({
|
||||
data: {
|
||||
const newData = await this.client.rest.patch(Routes.channel(this.id), {
|
||||
body: {
|
||||
name: (data.name ?? this.name).trim(),
|
||||
type: data.type,
|
||||
topic: data.topic,
|
||||
@@ -411,7 +411,8 @@ class GuildChannel extends Channel {
|
||||
position,
|
||||
relative,
|
||||
this.guild._sortedChannels(this),
|
||||
this.client.api.guilds(this.guild.id).channels,
|
||||
this.client,
|
||||
Routes.guildChannels(this.guild.id),
|
||||
reason,
|
||||
);
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
@@ -534,7 +535,7 @@ class GuildChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.channels(this.id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.channel(this.id), { reason });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const BaseGuildEmoji = require('./BaseGuildEmoji');
|
||||
const { Error } = require('../errors');
|
||||
const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager');
|
||||
@@ -81,7 +82,7 @@ class GuildEmoji extends BaseGuildEmoji {
|
||||
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();
|
||||
const data = await this.client.rest.get(Routes.guildEmoji(this.guild.id, this.id));
|
||||
this._patch(data);
|
||||
return this.author;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ class GuildMember extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
avatarURL(options = {}) {
|
||||
return this.avatar && this.client.rest.cdn.GuildMemberAvatar(this.guild.id, this.id, this.avatar, options);
|
||||
return this.avatar && this.client.rest.cdn.guildMemberAvatar(this.guild.id, this.id, this.avatar, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const GuildPreviewEmoji = require('./GuildPreviewEmoji');
|
||||
const { Sticker } = require('./Sticker');
|
||||
@@ -138,7 +139,7 @@ class GuildPreview extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
splashURL(options = {}) {
|
||||
return this.splash && this.client.rest.cdn.Splash(this.id, this.splash, options);
|
||||
return this.splash && this.client.rest.cdn.splash(this.id, this.splash, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,7 +148,7 @@ class GuildPreview extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
discoverySplashURL(options = {}) {
|
||||
return this.discoverySplash && this.client.rest.cdn.DiscoverySplash(this.id, this.discoverySplash, options);
|
||||
return this.discoverySplash && this.client.rest.cdn.discoverySplash(this.id, this.discoverySplash, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +157,7 @@ class GuildPreview extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.Icon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.icon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +165,7 @@ class GuildPreview extends Base {
|
||||
* @returns {Promise<GuildPreview>}
|
||||
*/
|
||||
async fetch() {
|
||||
const data = await this.client.api.guilds(this.id).preview.get();
|
||||
const data = await this.client.rest.get(Routes.guildPreview(this.id));
|
||||
this._patch(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { GuildScheduledEventStatus, GuildScheduledEventEntityType } = require('discord-api-types/v9');
|
||||
const { GuildScheduledEventStatus, GuildScheduledEventEntityType, RouteBases } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { Error } = require('../errors');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a scheduled event in a {@link Guild}.
|
||||
@@ -216,7 +215,7 @@ class GuildScheduledEvent extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get url() {
|
||||
return Endpoints.scheduledEvent(this.client.options.http.scheduledEvent, this.guildId, this.id);
|
||||
return `${RouteBases.scheduledEvent}/${this.guildId}/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +239,7 @@ class GuildScheduledEvent extends Base {
|
||||
if (!channelId) throw new Error('GUILD_CHANNEL_RESOLVE');
|
||||
}
|
||||
const invite = await this.guild.invites.create(channelId, options);
|
||||
return Endpoints.invite(this.client.options.http.invite, invite.code, this.id);
|
||||
return `${RouteBases.invite}/${invite.code}?event=${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { setTimeout, clearTimeout } = require('node:timers');
|
||||
const { RouteBases, Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { Events } = require('../util/Constants');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
@@ -114,8 +115,8 @@ class GuildTemplate extends Base {
|
||||
*/
|
||||
async createGuild(name, icon) {
|
||||
const { client } = this;
|
||||
const data = await client.api.guilds.templates(this.code).post({
|
||||
data: {
|
||||
const data = await client.rest.post(Routes.template(this.code), {
|
||||
body: {
|
||||
name,
|
||||
icon: await DataResolver.resolveImage(icon),
|
||||
},
|
||||
@@ -157,7 +158,9 @@ class GuildTemplate extends Base {
|
||||
* @returns {Promise<GuildTemplate>}
|
||||
*/
|
||||
async edit({ name, description } = {}) {
|
||||
const data = await this.client.api.guilds(this.guildId).templates(this.code).patch({ data: { name, description } });
|
||||
const data = await this.client.rest.patch(Routes.guildTemplate(this.guildId, this.code), {
|
||||
body: { name, description },
|
||||
});
|
||||
return this._patch(data);
|
||||
}
|
||||
|
||||
@@ -166,7 +169,7 @@ class GuildTemplate extends Base {
|
||||
* @returns {Promise<GuildTemplate>}
|
||||
*/
|
||||
async delete() {
|
||||
await this.client.api.guilds(this.guildId).templates(this.code).delete();
|
||||
await this.client.rest.delete(Routes.guildTemplate(this.guildId, this.code));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ class GuildTemplate extends Base {
|
||||
* @returns {Promise<GuildTemplate>}
|
||||
*/
|
||||
async sync() {
|
||||
const data = await this.client.api.guilds(this.guildId).templates(this.code).put();
|
||||
const data = await this.client.rest.put(Routes.guildTemplate(this.guildId, this.code));
|
||||
return this._patch(data);
|
||||
}
|
||||
|
||||
@@ -212,7 +215,7 @@ class GuildTemplate extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get url() {
|
||||
return `${this.client.options.http.template}/${this.code}`;
|
||||
return `${RouteBases.template}/${this.code}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const IntegrationApplication = require('./IntegrationApplication');
|
||||
|
||||
@@ -191,7 +192,7 @@ class Integration extends Base {
|
||||
* @param {string} [reason] Reason for deleting this integration
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.guilds(this.guild.id).integrations(this.id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.guildIntegration(this.guild.id, this.id), { reason });
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const { RouteBases, Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { GuildScheduledEvent } = require('./GuildScheduledEvent');
|
||||
const IntegrationApplication = require('./IntegrationApplication');
|
||||
const InviteStageInstance = require('./InviteStageInstance');
|
||||
const { Error } = require('../errors');
|
||||
const { Endpoints } = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
|
||||
/**
|
||||
@@ -267,7 +267,7 @@ class Invite extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get url() {
|
||||
return Endpoints.invite(this.client.options.http.invite, this.code);
|
||||
return `${RouteBases.invite}/${this.code}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +276,7 @@ class Invite extends Base {
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.invites[this.code].delete({ reason });
|
||||
await this.client.rest.delete(Routes.invite(this.code), { reason });
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,21 +29,14 @@ class MessagePayload {
|
||||
this.options = options;
|
||||
|
||||
/**
|
||||
* Data sendable to the API
|
||||
* Body sendable to the API
|
||||
* @type {?APIMessage}
|
||||
*/
|
||||
this.data = null;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageFile
|
||||
* @property {Buffer|string|Stream} attachment The original attachment that generated this file
|
||||
* @property {string} name The name of this file
|
||||
* @property {Buffer|Stream} file The file to be sent to the API
|
||||
*/
|
||||
this.body = null;
|
||||
|
||||
/**
|
||||
* Files sendable to the API
|
||||
* @type {?MessageFile[]}
|
||||
* @type {?RawFile[]}
|
||||
*/
|
||||
this.files = null;
|
||||
}
|
||||
@@ -117,10 +110,10 @@ class MessagePayload {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves data.
|
||||
* Resolves the body.
|
||||
* @returns {MessagePayload}
|
||||
*/
|
||||
resolveData() {
|
||||
resolveBody() {
|
||||
if (this.data) return this;
|
||||
const isInteraction = this.isInteraction;
|
||||
const isWebhook = this.isWebhook;
|
||||
@@ -189,7 +182,7 @@ class MessagePayload {
|
||||
this.options.attachments = attachments;
|
||||
}
|
||||
|
||||
this.data = {
|
||||
this.body = {
|
||||
content,
|
||||
tts,
|
||||
nonce,
|
||||
@@ -221,11 +214,11 @@ class MessagePayload {
|
||||
/**
|
||||
* Resolves a single file into an object sendable to the API.
|
||||
* @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file
|
||||
* @returns {Promise<MessageFile>}
|
||||
* @returns {Promise<RawFile>}
|
||||
*/
|
||||
static async resolveFile(fileLike) {
|
||||
let attachment;
|
||||
let name;
|
||||
let fileName;
|
||||
|
||||
const findName = thing => {
|
||||
if (typeof thing === 'string') {
|
||||
@@ -243,14 +236,14 @@ class MessagePayload {
|
||||
typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function';
|
||||
if (ownAttachment) {
|
||||
attachment = fileLike;
|
||||
name = findName(attachment);
|
||||
fileName = findName(attachment);
|
||||
} else {
|
||||
attachment = fileLike.attachment;
|
||||
name = fileLike.name ?? findName(attachment);
|
||||
fileName = fileLike.name ?? findName(attachment);
|
||||
}
|
||||
|
||||
const resource = await DataResolver.resolveFile(attachment);
|
||||
return { attachment, name, file: resource };
|
||||
const fileData = await DataResolver.resolveFile(attachment);
|
||||
return { fileData, fileName };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,3 +273,8 @@ module.exports = MessagePayload;
|
||||
* @external APIMessage
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#message-object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external RawFile
|
||||
* @see {@link https://discord.js.org/#/docs/rest/main/typedef/RawFile}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const GuildEmoji = require('./GuildEmoji');
|
||||
const ReactionEmoji = require('./ReactionEmoji');
|
||||
const ReactionUserManager = require('../managers/ReactionUserManager');
|
||||
@@ -56,11 +57,9 @@ class MessageReaction {
|
||||
* @returns {Promise<MessageReaction>}
|
||||
*/
|
||||
async remove() {
|
||||
await this.client.api
|
||||
.channels(this.message.channelId)
|
||||
.messages(this.message.id)
|
||||
.reactions(this._emoji.identifier)
|
||||
.delete();
|
||||
await this.client.rest.delete(
|
||||
Routes.channelMessageReaction(this.message.channelId, this.message.id, this._emoji.identifier),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const BaseGuildTextChannel = require('./BaseGuildTextChannel');
|
||||
const { Error } = require('../errors');
|
||||
|
||||
@@ -23,7 +24,7 @@ class NewsChannel extends BaseGuildTextChannel {
|
||||
async addFollower(channel, reason) {
|
||||
const channelId = this.guild.channels.resolveId(channel);
|
||||
if (!channelId) throw new Error('GUILD_CHANNEL_RESOLVE');
|
||||
await this.client.api.channels(this.id).followers.post({ data: { webhook_channel_id: channelId }, reason });
|
||||
await this.client.rest.post(Routes.channelFollowers(this.id), { body: { webhook_channel_id: channelId }, reason });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class PartialGroupDMChannel extends Channel {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.GDMIcon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.channelIcon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
delete() {
|
||||
|
||||
@@ -361,7 +361,7 @@ class RichPresenceAssets {
|
||||
}
|
||||
}
|
||||
|
||||
return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.smallImage, options);
|
||||
return this.activity.presence.client.rest.cdn.appAsset(this.activity.applicationId, this.smallImage, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,7 +387,7 @@ class RichPresenceAssets {
|
||||
}
|
||||
}
|
||||
|
||||
return this.activity.presence.client.rest.cdn.AppAsset(this.activity.applicationId, this.largeImage, options);
|
||||
return this.activity.presence.client.rest.cdn.appAsset(this.activity.applicationId, this.largeImage, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { Error } = require('../errors');
|
||||
const Permissions = require('../util/Permissions');
|
||||
@@ -364,7 +365,8 @@ class Role extends Base {
|
||||
position,
|
||||
relative,
|
||||
this.guild._sortedRoles(),
|
||||
this.client.api.guilds(this.guild.id).roles,
|
||||
this.client,
|
||||
Routes.guildRoles(this.guild.id),
|
||||
reason,
|
||||
);
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
@@ -395,7 +397,7 @@ class Role extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.RoleIcon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.roleIcon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { Routes, StickerFormatType } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
|
||||
/**
|
||||
@@ -161,7 +162,7 @@ class Sticker extends Base {
|
||||
* @type {string}
|
||||
*/
|
||||
get url() {
|
||||
return this.client.rest.cdn.Sticker(this.id, this.format);
|
||||
return this.client.rest.cdn.sticker(this.id, this.format === StickerFormatType.Lottie ? 'json' : 'png');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ class Sticker extends Base {
|
||||
* @returns {Promise<Sticker>}
|
||||
*/
|
||||
async fetch() {
|
||||
const data = await this.client.api.stickers(this.id).get();
|
||||
const data = await this.client.rest.get(Routes.sticker(this.id));
|
||||
this._patch(data);
|
||||
return this;
|
||||
}
|
||||
@@ -190,7 +191,7 @@ class Sticker extends Base {
|
||||
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();
|
||||
const data = await this.client.rest.get(Routes.guildSticker(this.guildId, this.id));
|
||||
this._patch(data);
|
||||
return this.user;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class StickerPack extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
bannerURL(options = {}) {
|
||||
return this.bannerId && this.client.rest.cdn.StickerPackBanner(this.bannerId, options);
|
||||
return this.bannerId && this.client.rest.cdn.stickerPackBanner(this.bannerId, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class Team extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.TeamIcon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.teamIcon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const { Channel } = require('./Channel');
|
||||
const TextBasedChannel = require('./interfaces/TextBasedChannel');
|
||||
const { RangeError } = require('../errors');
|
||||
@@ -322,8 +322,8 @@ class ThreadChannel extends Channel {
|
||||
autoArchiveDuration = 4320;
|
||||
}
|
||||
}
|
||||
const newData = await this.client.api.channels(this.id).patch({
|
||||
data: {
|
||||
const newData = await this.client.rest.patch(Routes.channel(this.id), {
|
||||
body: {
|
||||
name: (data.name ?? this.name).trim(),
|
||||
archived: data.archived,
|
||||
auto_archive_duration: autoArchiveDuration,
|
||||
@@ -540,7 +540,7 @@ class ThreadChannel extends Channel {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.channels(this.id).delete({ reason });
|
||||
await this.client.rest.delete(Routes.channel(this.id), { reason });
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ class User extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
avatarURL(options = {}) {
|
||||
return this.avatar && this.client.rest.cdn.Avatar(this.id, this.avatar, options);
|
||||
return this.avatar && this.client.rest.cdn.avatar(this.id, this.avatar, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +153,7 @@ class User extends Base {
|
||||
* @readonly
|
||||
*/
|
||||
get defaultAvatarURL() {
|
||||
return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
|
||||
return this.client.rest.cdn.defaultAvatar(this.discriminator % 5);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +183,7 @@ class User extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
bannerURL(options = {}) {
|
||||
return this.banner && this.client.rest.cdn.Banner(this.id, this.banner, options);
|
||||
return this.banner && this.client.rest.cdn.banner(this.id, this.banner, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const { Error, TypeError } = require('../errors');
|
||||
|
||||
@@ -222,8 +222,8 @@ class VoiceState extends Base {
|
||||
|
||||
if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
|
||||
|
||||
await this.client.api.guilds(this.guild.id, 'voice-states', '@me').patch({
|
||||
data: {
|
||||
await this.client.rest.patch(Routes.guildVoiceState(this.guild.id), {
|
||||
body: {
|
||||
channel_id: this.channelId,
|
||||
request_to_speak_timestamp: request ? new Date().toISOString() : null,
|
||||
},
|
||||
@@ -254,8 +254,8 @@ class VoiceState extends Base {
|
||||
|
||||
const target = this.client.user.id === this.id ? '@me' : this.id;
|
||||
|
||||
await this.client.api.guilds(this.guild.id, 'voice-states', target).patch({
|
||||
data: {
|
||||
await this.client.rest.patch(Routes.guildVoiceState(this.guild.id, target), {
|
||||
body: {
|
||||
channel_id: this.channelId,
|
||||
suppress: suppressed,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { WebhookType } = require('discord-api-types/v9');
|
||||
const { Routes, WebhookType } = require('discord-api-types/v9');
|
||||
const MessagePayload = require('./MessagePayload');
|
||||
const { Error } = require('../errors');
|
||||
const DataResolver = require('../util/DataResolver');
|
||||
@@ -194,18 +194,19 @@ class Webhook {
|
||||
let messagePayload;
|
||||
|
||||
if (options instanceof MessagePayload) {
|
||||
messagePayload = options.resolveData();
|
||||
messagePayload = options.resolveBody();
|
||||
} else {
|
||||
messagePayload = MessagePayload.create(this, options).resolveData();
|
||||
messagePayload = MessagePayload.create(this, options).resolveBody();
|
||||
}
|
||||
|
||||
const { data, files } = await messagePayload.resolveFiles();
|
||||
const d = await this.client.api.webhooks(this.id, this.token).post({
|
||||
data,
|
||||
files,
|
||||
query: { thread_id: messagePayload.options.threadId, wait: true },
|
||||
auth: false,
|
||||
});
|
||||
const query = new URLSearchParams({ wait: true });
|
||||
|
||||
if (messagePayload.options.threadId) {
|
||||
query.set('thread_id', messagePayload.options.threadId);
|
||||
}
|
||||
|
||||
const { body, files } = await messagePayload.resolveFiles();
|
||||
const d = await this.client.rest.post(Routes.webhook(this.id, this.token), { body, files, query, auth: false });
|
||||
return this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ?? d;
|
||||
}
|
||||
|
||||
@@ -230,10 +231,10 @@ class Webhook {
|
||||
async sendSlackMessage(body) {
|
||||
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
|
||||
|
||||
const data = await this.client.api.webhooks(this.id, this.token).slack.post({
|
||||
query: { wait: true },
|
||||
const data = await this.client.rest.post(Routes.webhookPlatform(this.id, this.token, 'slack'), {
|
||||
query: new URLSearchParams({ wait: true }),
|
||||
auth: false,
|
||||
data: body,
|
||||
body,
|
||||
});
|
||||
return data.toString() === 'ok';
|
||||
}
|
||||
@@ -257,8 +258,8 @@ class Webhook {
|
||||
avatar = await DataResolver.resolveImage(avatar);
|
||||
}
|
||||
channel &&= channel.id ?? channel;
|
||||
const data = await this.client.api.webhooks(this.id, channel ? undefined : this.token).patch({
|
||||
data: { name, avatar, channel_id: channel },
|
||||
const data = await this.client.rest.patch(Routes.webhook(this.id, channel ? undefined : this.token), {
|
||||
body: { name, avatar, channel_id: channel },
|
||||
reason,
|
||||
auth: !this.token || Boolean(channel),
|
||||
});
|
||||
@@ -287,15 +288,14 @@ class Webhook {
|
||||
async fetchMessage(message, { cache = true, threadId } = {}) {
|
||||
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
|
||||
|
||||
const data = await this.client.api
|
||||
.webhooks(this.id, this.token)
|
||||
.messages(message)
|
||||
.get({
|
||||
query: {
|
||||
thread_id: threadId,
|
||||
},
|
||||
auth: false,
|
||||
});
|
||||
const data = await this.client.rest.get(Routes.webhookMessage(this.id, this.token, message), {
|
||||
query: threadId
|
||||
? new URLSearchParams({
|
||||
thread_id: threadId,
|
||||
})
|
||||
: undefined,
|
||||
auth: false,
|
||||
});
|
||||
return this.client.channels?.cache.get(data.channel_id)?.messages._add(data, cache) ?? data;
|
||||
}
|
||||
|
||||
@@ -314,19 +314,21 @@ class Webhook {
|
||||
if (options instanceof MessagePayload) messagePayload = options;
|
||||
else messagePayload = MessagePayload.create(this, options);
|
||||
|
||||
const { data, files } = await messagePayload.resolveData().resolveFiles();
|
||||
const { body, files } = await messagePayload.resolveData().resolveFiles();
|
||||
|
||||
const d = await this.client.api
|
||||
.webhooks(this.id, this.token)
|
||||
.messages(typeof message === 'string' ? message : message.id)
|
||||
.patch({
|
||||
data,
|
||||
const d = await this.client.rest.patch(
|
||||
Routes.webhookMessage(this.id, this.token, typeof message === 'string' ? message : message.id),
|
||||
{
|
||||
body,
|
||||
files,
|
||||
query: {
|
||||
thread_id: messagePayload.options.threadId,
|
||||
},
|
||||
query: messagePayload.options.threadId
|
||||
? new URLSearchParams({
|
||||
thread_id: messagePayload.options.threadId,
|
||||
})
|
||||
: undefined,
|
||||
auth: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const messageManager = this.client.channels?.cache.get(d.channel_id)?.messages;
|
||||
if (!messageManager) return d;
|
||||
@@ -345,7 +347,7 @@ class Webhook {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.webhooks(this.id, this.token).delete({ reason, auth: !this.token });
|
||||
await this.client.rest.delete(Routes.webhook(this.id, this.token), { reason, auth: !this.token });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,15 +359,17 @@ class Webhook {
|
||||
async deleteMessage(message, threadId) {
|
||||
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
|
||||
|
||||
await this.client.api
|
||||
.webhooks(this.id, this.token)
|
||||
.messages(typeof message === 'string' ? message : message.id)
|
||||
.delete({
|
||||
query: {
|
||||
thread_id: threadId,
|
||||
},
|
||||
await this.client.rest.delete(
|
||||
Routes.webhookMessage(this.id, this.token, typeof message === 'string' ? message : message.id),
|
||||
{
|
||||
query: threadId
|
||||
? new URLSearchParams({
|
||||
thread_id: threadId,
|
||||
})
|
||||
: undefined,
|
||||
auth: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,7 +396,7 @@ class Webhook {
|
||||
* @readonly
|
||||
*/
|
||||
get url() {
|
||||
return this.client.options.http.api + this.client.api.webhooks(this.id, this.token);
|
||||
return this.client.options.rest.api + Routes.webhook(this.id, this.token);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,7 +405,7 @@ class Webhook {
|
||||
* @returns {?string}
|
||||
*/
|
||||
avatarURL(options = {}) {
|
||||
return this.avatar && this.client.rest.cdn.Avatar(this.id, this.avatar, options);
|
||||
return this.avatar && this.client.rest.cdn.avatar(this.id, this.avatar, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const Base = require('./Base');
|
||||
const WidgetMember = require('./WidgetMember');
|
||||
|
||||
@@ -77,7 +78,7 @@ class Widget extends Base {
|
||||
* @returns {Promise<Widget>}
|
||||
*/
|
||||
async fetch() {
|
||||
const data = await this.client.api.guilds(this.id, 'widget.json').get();
|
||||
const data = await this.client.rest.get(Routes.guildWidgetJSON(this.id));
|
||||
this._patch(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class Application extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
iconURL(options = {}) {
|
||||
return this.icon && this.client.rest.cdn.AppIcon(this.id, this.icon, options);
|
||||
return this.icon && this.client.rest.cdn.appIcon(this.id, this.icon, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +84,7 @@ class Application extends Base {
|
||||
* @returns {?string}
|
||||
*/
|
||||
coverURL(options = {}) {
|
||||
return this.cover && this.client.rest.cdn.AppIcon(this.id, this.cover, options);
|
||||
return this.cover && this.client.rest.cdn.appIcon(this.id, this.cover, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { InteractionResponseType } = require('discord-api-types/v9');
|
||||
const { InteractionResponseType, Routes } = require('discord-api-types/v9');
|
||||
const { Error } = require('../../errors');
|
||||
const MessageFlags = require('../../util/MessageFlags');
|
||||
const MessagePayload = require('../MessagePayload');
|
||||
@@ -56,8 +56,8 @@ class InteractionResponses {
|
||||
async deferReply(options = {}) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
this.ephemeral = options.ephemeral ?? false;
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.DeferredChannelMessageWithSource,
|
||||
data: {
|
||||
flags: options.ephemeral ? MessageFlags.FLAGS.EPHEMERAL : undefined,
|
||||
@@ -96,10 +96,10 @@ class InteractionResponses {
|
||||
if (options instanceof MessagePayload) messagePayload = options;
|
||||
else messagePayload = MessagePayload.create(this, options);
|
||||
|
||||
const { data, files } = await messagePayload.resolveData().resolveFiles();
|
||||
const { body: data, files } = await messagePayload.resolveBody().resolveFiles();
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.ChannelMessageWithSource,
|
||||
data,
|
||||
},
|
||||
@@ -180,8 +180,8 @@ class InteractionResponses {
|
||||
*/
|
||||
async deferUpdate(options = {}) {
|
||||
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.DeferredMessageUpdate,
|
||||
},
|
||||
auth: false,
|
||||
@@ -211,10 +211,10 @@ class InteractionResponses {
|
||||
if (options instanceof MessagePayload) messagePayload = options;
|
||||
else messagePayload = MessagePayload.create(this, options);
|
||||
|
||||
const { data, files } = await messagePayload.resolveData().resolveFiles();
|
||||
const { body: data, files } = await messagePayload.resolveBody().resolveFiles();
|
||||
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.UpdateMessage,
|
||||
data,
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { InteractionType } = require('discord-api-types/v9');
|
||||
const { InteractionType, Routes } = require('discord-api-types/v9');
|
||||
const { TypeError, Error } = require('../../errors');
|
||||
const InteractionCollector = require('../InteractionCollector');
|
||||
const MessageCollector = require('../MessageCollector');
|
||||
@@ -166,13 +166,13 @@ class TextBasedChannel {
|
||||
let messagePayload;
|
||||
|
||||
if (options instanceof MessagePayload) {
|
||||
messagePayload = options.resolveData();
|
||||
messagePayload = options.resolveBody();
|
||||
} else {
|
||||
messagePayload = MessagePayload.create(this, options).resolveData();
|
||||
messagePayload = MessagePayload.create(this, options).resolveBody();
|
||||
}
|
||||
|
||||
const { data, files } = await messagePayload.resolveFiles();
|
||||
const d = await this.client.api.channels[this.id].messages.post({ data, files });
|
||||
const { body, files } = await messagePayload.resolveFiles();
|
||||
const d = await this.client.rest.post(Routes.channelMessages(this.id), { body, files });
|
||||
|
||||
return this.messages.cache.get(d.id) ?? this.messages._add(d);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class TextBasedChannel {
|
||||
* channel.sendTyping();
|
||||
*/
|
||||
async sendTyping() {
|
||||
await this.client.api.channels(this.id).typing.post();
|
||||
await this.client.rest.post(Routes.channelTyping(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ class TextBasedChannel {
|
||||
}
|
||||
if (messageIds.length === 0) return new Collection();
|
||||
if (messageIds.length === 1) {
|
||||
await this.client.api.channels(this.id).messages(messageIds[0]).delete();
|
||||
await this.client.rest.delete(Routes.channelMessage(this.id, messageIds[0]));
|
||||
const message = this.client.actions.MessageDelete.getMessage(
|
||||
{
|
||||
message_id: messageIds[0],
|
||||
@@ -307,7 +307,7 @@ class TextBasedChannel {
|
||||
);
|
||||
return message ? new Collection([[message.id, message]]) : new Collection();
|
||||
}
|
||||
await this.client.api.channels[this.id].messages['bulk-delete'].post({ data: { messages: messageIds } });
|
||||
await this.client.rest.post(Routes.channelBulkDelete(this.id), { body: { messages: messageIds } });
|
||||
return messageIds.reduce(
|
||||
(col, id) =>
|
||||
col.set(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const process = require('node:process');
|
||||
const { ChannelType, MessageType, StickerFormatType } = require('discord-api-types/v9');
|
||||
const { ChannelType, MessageType } = require('discord-api-types/v9');
|
||||
const Package = (exports.Package = require('../../package.json'));
|
||||
const { Error, RangeError, TypeError } = require('../errors');
|
||||
|
||||
exports.UserAgent = `DiscordBot (${Package.homepage}, ${Package.version}) Node.js/${process.version}`;
|
||||
|
||||
@@ -16,84 +15,6 @@ exports.WSCodes = {
|
||||
4014: 'DISALLOWED_INTENTS',
|
||||
};
|
||||
|
||||
const AllowedImageFormats = ['webp', 'png', 'jpg', 'jpeg'];
|
||||
|
||||
const AllowedImageSizes = [16, 32, 56, 64, 96, 128, 256, 300, 512, 600, 1024, 2048, 4096];
|
||||
|
||||
function makeImageUrl(root, { hash, format = 'webp', forceStatic = false, size } = {}) {
|
||||
if (!['undefined', 'number'].includes(typeof size)) throw new TypeError('INVALID_TYPE', 'size', 'number');
|
||||
if (!AllowedImageFormats.includes(format)) throw new Error('IMAGE_FORMAT', format);
|
||||
if (size && !AllowedImageSizes.includes(size)) throw new RangeError('IMAGE_SIZE', size);
|
||||
if (!forceStatic && hash?.startsWith('a_')) format = 'gif';
|
||||
return `${root}${hash ? `/${hash}` : ''}.${format}${size ? `?size=${size}` : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of image sizes:
|
||||
* * `16`
|
||||
* * `32`
|
||||
* * `56`
|
||||
* * `64`
|
||||
* * `96`
|
||||
* * `128`
|
||||
* * `256`
|
||||
* * `300`
|
||||
* * `512`
|
||||
* * `600`
|
||||
* * `1024`
|
||||
* * `2048`
|
||||
* * `4096`
|
||||
* @typedef {number} ImageSize
|
||||
*/
|
||||
|
||||
/**
|
||||
* A list of image formats:
|
||||
* * `webp`
|
||||
* * `png`
|
||||
* * `jpg`
|
||||
* * `jpeg`
|
||||
* @typedef {string} ImageFormat
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for image URLs.
|
||||
* @typedef {Object} ImageURLOptions
|
||||
* @property {ImageFormat} [format='webp'] An image format.
|
||||
* @property {boolean} [forceStatic=false] If `true`, the format will be as specified.
|
||||
* If `false`, `format` may be a `gif` if animated.
|
||||
* @property {ImageSize} [size] An image size.
|
||||
*/
|
||||
|
||||
// https://discord.com/developers/docs/reference#image-formatting-cdn-endpoints
|
||||
exports.Endpoints = {
|
||||
CDN(root) {
|
||||
return {
|
||||
Emoji: (emojiId, format) => `${root}/emojis/${emojiId}.${format}`,
|
||||
DefaultAvatar: discriminator => `${root}/embed/avatars/${discriminator}.png`,
|
||||
Avatar: (userId, hash, options) => makeImageUrl(`${root}/avatars/${userId}`, { hash, ...options }),
|
||||
GuildMemberAvatar: (guildId, memberId, hash, options) =>
|
||||
makeImageUrl(`${root}/guilds/${guildId}/users/${memberId}/avatars`, { hash, ...options }),
|
||||
Banner: (id, hash, options) => makeImageUrl(`${root}/banners/${id}`, { hash, ...options }),
|
||||
Icon: (guildId, hash, options) => makeImageUrl(`${root}/icons/${guildId}`, { hash, ...options }),
|
||||
AppIcon: (appId, hash, options) => makeImageUrl(`${root}/app-icons/${appId}`, { hash, ...options }),
|
||||
AppAsset: (appId, hash, options) => makeImageUrl(`${root}/app-assets/${appId}`, { hash, ...options }),
|
||||
StickerPackBanner: (bannerId, options) =>
|
||||
makeImageUrl(`${root}/app-assets/710982414301790216/store/${bannerId}`, options),
|
||||
GDMIcon: (channelId, hash, options) => makeImageUrl(`${root}/channel-icons/${channelId}`, { hash, ...options }),
|
||||
Splash: (guildId, hash, options) => makeImageUrl(`${root}/splashes/${guildId}`, { hash, ...options }),
|
||||
DiscoverySplash: (guildId, hash, options) =>
|
||||
makeImageUrl(`${root}/discovery-splashes/${guildId}`, { hash, ...options }),
|
||||
TeamIcon: (teamId, hash, options) => makeImageUrl(`${root}/team-icons/${teamId}`, { hash, ...options }),
|
||||
Sticker: (stickerId, format) =>
|
||||
`${root}/stickers/${stickerId}.${format === StickerFormatType.Lottie ? 'json' : 'png'}`,
|
||||
RoleIcon: (roleId, hash, options) => makeImageUrl(`${root}/role-icons/${roleId}`, { hash, ...options }),
|
||||
};
|
||||
},
|
||||
invite: (root, code, eventId) => (eventId ? `${root}/${code}?event=${eventId}` : `${root}/${code}`),
|
||||
scheduledEvent: (root, guildId, eventId) => `${root}/${guildId}/${eventId}`,
|
||||
botGateway: '/gateway/bot',
|
||||
};
|
||||
|
||||
/**
|
||||
* The current status of the client. Here are the available statuses:
|
||||
* * READY: 0
|
||||
@@ -135,10 +56,6 @@ exports.Opcodes = {
|
||||
};
|
||||
|
||||
exports.Events = {
|
||||
RATE_LIMIT: 'rateLimit',
|
||||
INVALID_REQUEST_WARNING: 'invalidRequestWarning',
|
||||
API_RESPONSE: 'apiResponse',
|
||||
API_REQUEST: 'apiRequest',
|
||||
CLIENT_READY: 'ready',
|
||||
GUILD_CREATE: 'guildCreate',
|
||||
GUILD_DELETE: 'guildDelete',
|
||||
|
||||
@@ -66,7 +66,7 @@ class DataResolver extends null {
|
||||
if (typeof image === 'string' && image.startsWith('data:')) {
|
||||
return image;
|
||||
}
|
||||
const file = await this.resolveFileAsBuffer(image);
|
||||
const file = await this.resolveFile(image);
|
||||
return DataResolver.resolveBase64(file);
|
||||
}
|
||||
|
||||
@@ -102,12 +102,19 @@ class DataResolver extends null {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a BufferResolvable to a Buffer or a Stream.
|
||||
* Resolves a BufferResolvable to a Buffer.
|
||||
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
|
||||
* @returns {Promise<Buffer|Stream>}
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
static async resolveFile(resource) {
|
||||
if (Buffer.isBuffer(resource) || resource instanceof stream.Readable) return resource;
|
||||
if (Buffer.isBuffer(resource)) return resource;
|
||||
|
||||
if (resource instanceof stream.Readable) {
|
||||
const buffers = [];
|
||||
for await (const data of resource) buffers.push(data);
|
||||
return Buffer.concat(buffers);
|
||||
}
|
||||
|
||||
if (typeof resource === 'string') {
|
||||
if (/^https?:\/\//.test(resource)) {
|
||||
const res = await fetch(resource);
|
||||
@@ -126,20 +133,6 @@ class DataResolver extends null {
|
||||
|
||||
throw new TypeError('REQ_RESOURCE_TYPE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a BufferResolvable to a Buffer.
|
||||
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
static async resolveFileAsBuffer(resource) {
|
||||
const file = await this.resolveFile(resource);
|
||||
if (Buffer.isBuffer(file)) return file;
|
||||
|
||||
const buffers = [];
|
||||
for await (const data of file) buffers.push(data);
|
||||
return Buffer.concat(buffers);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DataResolver;
|
||||
|
||||
@@ -1,24 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const process = require('node:process');
|
||||
|
||||
/**
|
||||
* Rate limit data
|
||||
* @typedef {Object} RateLimitData
|
||||
* @property {number} timeout Time until this rate limit ends, in milliseconds
|
||||
* @property {number} limit The maximum amount of requests of this endpoint
|
||||
* @property {string} method The HTTP method of this request
|
||||
* @property {string} path The path of the request relative to the HTTP endpoint
|
||||
* @property {string} route The route of the request relative to the HTTP endpoint
|
||||
* @property {boolean} global Whether this is a global rate limit
|
||||
*/
|
||||
|
||||
/**
|
||||
* Whether this rate limit should throw an Error
|
||||
* @typedef {Function} RateLimitQueueFilter
|
||||
* @param {RateLimitData} rateLimitData The data of this rate limit
|
||||
* @returns {boolean|Promise<boolean>}
|
||||
*/
|
||||
const { DefaultRestOptions } = require('@discordjs/rest');
|
||||
|
||||
/**
|
||||
* @typedef {Function} CacheFactory
|
||||
@@ -40,36 +23,18 @@ const process = require('node:process');
|
||||
* <warn>Overriding the cache used in `GuildManager`, `ChannelManager`, `GuildChannelManager`, `RoleManager`,
|
||||
* and `PermissionOverwriteManager` is unsupported and **will** break functionality</warn>
|
||||
* @property {MessageMentionOptions} [allowedMentions] Default value for {@link MessageOptions#allowedMentions}
|
||||
* @property {number} [invalidRequestWarningInterval=0] The number of invalid REST requests (those that return
|
||||
* 401, 403, or 429) in a 10 minute window between emitted warnings (0 for no warnings). That is, if set to 500,
|
||||
* warnings will be emitted at invalid request number 500, 1000, 1500, and so on.
|
||||
* @property {PartialType[]} [partials] Structures allowed to be partial. This means events can be emitted even when
|
||||
* they're missing all the data for a particular structure. See the "Partial Structures" topic on the
|
||||
* [guide](https://discordjs.guide/popular-topics/partials.html) for some
|
||||
* important usage information, as partials require you to put checks in place when handling data.
|
||||
* @property {number} [restTimeOffset=500] Extra time in milliseconds to wait before continuing to make REST
|
||||
* requests (higher values will reduce rate-limiting errors on bad connections)
|
||||
* @property {number} [restRequestTimeout=15000] Time to wait before cancelling a REST request, in milliseconds
|
||||
* @property {number} [restSweepInterval=60] How frequently to delete inactive request buckets, in seconds
|
||||
* (or 0 for never)
|
||||
* @property {number} [restGlobalRateLimit=0] How many requests to allow sending per second (0 for unlimited, 50 for
|
||||
* the standard global limit used by Discord)
|
||||
* @property {string[]|RateLimitQueueFilter} [rejectOnRateLimit] Decides how rate limits and pre-emptive throttles
|
||||
* should be handled. If this option is an array containing the prefix of the request route (e.g. /channels to match any
|
||||
* route starting with /channels, such as /channels/222197033908436994/messages) or a function returning true, a
|
||||
* {@link RateLimitError} will be thrown. Otherwise the request will be queued for later
|
||||
* @property {number} [retryLimit=1] How many times to retry on 5XX errors
|
||||
* (Infinity for an indefinite amount of retries)
|
||||
* @property {boolean} [failIfNotExists=true] Default value for {@link ReplyMessageOptions#failIfNotExists}
|
||||
* @property {string[]} [userAgentSuffix] An array of additional bot info to be appended to the end of the required
|
||||
* [User Agent](https://discord.com/developers/docs/reference#user-agent) header
|
||||
* @property {PresenceData} [presence={}] Presence data to use upon login
|
||||
* @property {IntentsResolvable} intents Intents to enable for this connection
|
||||
* @property {number} [waitGuildTimeout=15_000] Time in milliseconds that Clients with the GUILDS intent should wait for
|
||||
* missing guilds to be received before starting the bot. If not specified, the default is 15 seconds.
|
||||
* @property {SweeperOptions} [sweepers={}] Options for cache sweeping
|
||||
* @property {WebsocketOptions} [ws] Options for the WebSocket
|
||||
* @property {HTTPOptions} [http] HTTP options
|
||||
* @property {RESTOptions} [rest] Options for the REST manager
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -95,26 +60,6 @@ const process = require('node:process');
|
||||
* sent in the initial guild member list, must be between 50 and 250
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTPS Agent options.
|
||||
* @typedef {Object} AgentOptions
|
||||
* @see {@link https://nodejs.org/api/https.html#https_class_https_agent}
|
||||
* @see {@link https://nodejs.org/api/http.html#http_new_agent_options}
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTP options
|
||||
* @typedef {Object} HTTPOptions
|
||||
* @property {number} [version=9] API version to use
|
||||
* @property {AgentOptions} [agent={}] HTTPS Agent options
|
||||
* @property {string} [api='https://discord.com/api'] Base URL of the API
|
||||
* @property {string} [cdn='https://cdn.discordapp.com'] Base URL of the CDN
|
||||
* @property {string} [invite='https://discord.gg'] Base URL of invites
|
||||
* @property {string} [template='https://discord.new'] Base URL of templates
|
||||
* @property {Object} [headers] Additional headers to send for all API requests
|
||||
* @property {string} [scheduledEvent='https://discord.com/events'] Base URL of guild scheduled events
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains various utilities for client options.
|
||||
*/
|
||||
@@ -128,15 +73,8 @@ class Options extends null {
|
||||
waitGuildTimeout: 15_000,
|
||||
shardCount: 1,
|
||||
makeCache: this.cacheWithLimits(this.defaultMakeCacheSettings),
|
||||
invalidRequestWarningInterval: 0,
|
||||
partials: [],
|
||||
restRequestTimeout: 15_000,
|
||||
restGlobalRateLimit: 0,
|
||||
retryLimit: 1,
|
||||
restTimeOffset: 500,
|
||||
restSweepInterval: 60,
|
||||
failIfNotExists: true,
|
||||
userAgentSuffix: [],
|
||||
presence: {},
|
||||
sweepers: this.defaultSweeperSettings,
|
||||
ws: {
|
||||
@@ -149,15 +87,7 @@ class Options extends null {
|
||||
},
|
||||
version: 9,
|
||||
},
|
||||
http: {
|
||||
agent: {},
|
||||
version: 9,
|
||||
api: 'https://discord.com/api',
|
||||
cdn: 'https://cdn.discordapp.com',
|
||||
invite: 'https://discord.gg',
|
||||
template: 'https://discord.new',
|
||||
scheduledEvent: 'https://discord.com/events',
|
||||
},
|
||||
rest: DefaultRestOptions,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -246,3 +176,8 @@ Options.defaultSweeperSettings = {
|
||||
};
|
||||
|
||||
module.exports = Options;
|
||||
|
||||
/**
|
||||
* @external RESTOptions
|
||||
* @see {@link https://discord.js.org/#/docs/rest/main/typedef/RESTOptions}
|
||||
*/
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
const { parse } = require('node:path');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { ChannelType } = require('discord-api-types/v9');
|
||||
const { ChannelType, RouteBases, Routes } = require('discord-api-types/v9');
|
||||
const fetch = require('node-fetch');
|
||||
const { Colors, Endpoints } = require('./Constants');
|
||||
const Options = require('./Options');
|
||||
const { Colors } = require('./Constants');
|
||||
const { Error: DiscordError, RangeError, TypeError } = require('../errors');
|
||||
const isObject = d => typeof d === 'object' && d !== null;
|
||||
|
||||
@@ -269,8 +268,7 @@ class Util extends null {
|
||||
*/
|
||||
static async fetchRecommendedShards(token, { guildsPerShard = 1_000, multipleOf = 1 } = {}) {
|
||||
if (!token) throw new DiscordError('TOKEN_MISSING');
|
||||
const defaults = Options.createDefault();
|
||||
const response = await fetch(`${defaults.http.api}/v${defaults.http.version}${Endpoints.botGateway}`, {
|
||||
const response = await fetch(RouteBases.api + Routes.gatewayBot(), {
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bot ${token.replace(/^Bot\s*/i, '')}` },
|
||||
});
|
||||
@@ -495,16 +493,17 @@ class Util extends null {
|
||||
* @param {number} position New position for the object
|
||||
* @param {boolean} relative Whether `position` is relative to its current position
|
||||
* @param {Collection<string, Channel|Role>} sorted A collection of the objects sorted properly
|
||||
* @param {APIRouter} route Route to call PATCH on
|
||||
* @param {Client} client The client to use to patch the data
|
||||
* @param {string} route Route to call PATCH on
|
||||
* @param {string} [reason] Reason for the change
|
||||
* @returns {Promise<Channel[]|Role[]>} Updated item list, with `id` and `position` properties
|
||||
* @private
|
||||
*/
|
||||
static async setPosition(item, position, relative, sorted, route, reason) {
|
||||
static async setPosition(item, position, relative, sorted, client, route, reason) {
|
||||
let updatedItems = [...sorted.values()];
|
||||
Util.moveElementInArray(updatedItems, item, position, relative);
|
||||
updatedItems = updatedItems.map((r, i) => ({ id: r.id, position: i }));
|
||||
await route.patch({ data: updatedItems, reason });
|
||||
await client.rest.patch(route, { body: updatedItems, reason });
|
||||
return updatedItems;
|
||||
}
|
||||
|
||||
|
||||
195
packages/discord.js/typings/index.d.ts
vendored
195
packages/discord.js/typings/index.d.ts
vendored
@@ -26,6 +26,7 @@ import {
|
||||
userMention,
|
||||
} from '@discordjs/builders';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import { ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
|
||||
import {
|
||||
APIActionRowComponent,
|
||||
APIApplicationCommand,
|
||||
@@ -81,8 +82,6 @@ import {
|
||||
} from 'discord-api-types/v9';
|
||||
import { ChildProcess } from 'node:child_process';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { AgentOptions } from 'node:https';
|
||||
import { Response } from 'node-fetch';
|
||||
import { Stream } from 'node:stream';
|
||||
import { MessagePort, Worker } from 'node:worker_threads';
|
||||
import * as WebSocket from 'ws';
|
||||
@@ -270,45 +269,11 @@ export abstract class Base {
|
||||
|
||||
export class BaseClient extends EventEmitter {
|
||||
public constructor(options?: ClientOptions | WebhookClientOptions);
|
||||
private readonly api: unknown;
|
||||
private rest: unknown;
|
||||
private decrementMaxListeners(): void;
|
||||
private incrementMaxListeners(): void;
|
||||
|
||||
public on<K extends keyof BaseClientEvents>(
|
||||
event: K,
|
||||
listener: (...args: BaseClientEvents[K]) => Awaitable<void>,
|
||||
): this;
|
||||
public on<S extends string | symbol>(
|
||||
event: Exclude<S, keyof BaseClientEvents>,
|
||||
listener: (...args: any[]) => Awaitable<void>,
|
||||
): this;
|
||||
|
||||
public once<K extends keyof BaseClientEvents>(
|
||||
event: K,
|
||||
listener: (...args: BaseClientEvents[K]) => Awaitable<void>,
|
||||
): this;
|
||||
public once<S extends string | symbol>(
|
||||
event: Exclude<S, keyof BaseClientEvents>,
|
||||
listener: (...args: any[]) => Awaitable<void>,
|
||||
): this;
|
||||
|
||||
public emit<K extends keyof BaseClientEvents>(event: K, ...args: BaseClientEvents[K]): boolean;
|
||||
public emit<S extends string | symbol>(event: Exclude<S, keyof BaseClientEvents>, ...args: unknown[]): boolean;
|
||||
|
||||
public off<K extends keyof BaseClientEvents>(
|
||||
event: K,
|
||||
listener: (...args: BaseClientEvents[K]) => Awaitable<void>,
|
||||
): this;
|
||||
public off<S extends string | symbol>(
|
||||
event: Exclude<S, keyof BaseClientEvents>,
|
||||
listener: (...args: any[]) => Awaitable<void>,
|
||||
): this;
|
||||
|
||||
public removeAllListeners<K extends keyof BaseClientEvents>(event?: K): this;
|
||||
public removeAllListeners<S extends string | symbol>(event?: Exclude<S, keyof BaseClientEvents>): this;
|
||||
|
||||
public options: ClientOptions | WebhookClientOptions;
|
||||
public rest: REST;
|
||||
public destroy(): void;
|
||||
public toJSON(...props: Record<string, boolean | string>[]): unknown;
|
||||
}
|
||||
@@ -819,8 +784,7 @@ export class DataResolver extends null {
|
||||
private constructor();
|
||||
public static resolveBase64(data: Base64Resolvable): string;
|
||||
public static resolveCode(data: string, regx: RegExp): string;
|
||||
public static resolveFile(resource: BufferResolvable | Stream): Promise<Buffer | Stream>;
|
||||
public static resolveFileAsBuffer(resource: BufferResolvable | Stream): Promise<Buffer>;
|
||||
public static resolveFile(resource: BufferResolvable | Stream): Promise<Buffer>;
|
||||
public static resolveImage(resource: BufferResolvable | Base64Resolvable): Promise<string | null>;
|
||||
public static resolveInviteCode(data: InviteResolvable): string;
|
||||
public static resolveGuildTemplateCode(data: GuildTemplateResolvable): string;
|
||||
@@ -871,17 +835,6 @@ export class EnumResolvers extends null {
|
||||
): IntegrationExpireBehavior;
|
||||
}
|
||||
|
||||
export class DiscordAPIError extends Error {
|
||||
private constructor(error: unknown, status: number, request: unknown);
|
||||
private static flattenErrors(obj: unknown, key: string): string[];
|
||||
|
||||
public code: number;
|
||||
public method: string;
|
||||
public path: string;
|
||||
public httpStatus: number;
|
||||
public requestData: HTTPErrorData;
|
||||
}
|
||||
|
||||
export class DMChannel extends TextBasedChannelMixin(Channel, ['bulkDelete']) {
|
||||
private constructor(client: Client, data?: RawDMChannelData);
|
||||
public messages: MessageManager;
|
||||
@@ -1261,22 +1214,6 @@ export class GuildPreviewEmoji extends BaseGuildEmoji {
|
||||
public roles: Snowflake[];
|
||||
}
|
||||
|
||||
export class HTTPError extends Error {
|
||||
private constructor(message: string, name: string, code: number, request: unknown);
|
||||
public code: number;
|
||||
public method: string;
|
||||
public name: string;
|
||||
public path: string;
|
||||
public requestData: HTTPErrorData;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:no-empty-interface - Merge RateLimitData into RateLimitError to not have to type it again
|
||||
export interface RateLimitError extends RateLimitData {}
|
||||
export class RateLimitError extends Error {
|
||||
private constructor(data: RateLimitData);
|
||||
public name: 'RateLimitError';
|
||||
}
|
||||
|
||||
export class Integration extends Base {
|
||||
private constructor(client: Client, data: RawIntegrationData, guild: Guild);
|
||||
public account: IntegrationAccount;
|
||||
@@ -1680,13 +1617,13 @@ export class MessageMentions {
|
||||
|
||||
export class MessagePayload {
|
||||
public constructor(target: MessageTarget, options: MessageOptions | WebhookMessageOptions);
|
||||
public data: RawMessagePayloadData | null;
|
||||
public body: RawMessagePayloadData | null;
|
||||
public readonly isUser: boolean;
|
||||
public readonly isWebhook: boolean;
|
||||
public readonly isMessage: boolean;
|
||||
public readonly isMessageManager: boolean;
|
||||
public readonly isInteraction: boolean;
|
||||
public files: HTTPAttachmentData[] | null;
|
||||
public files: RawFile[] | null;
|
||||
public options: MessageOptions | WebhookMessageOptions;
|
||||
public target: MessageTarget;
|
||||
|
||||
@@ -1695,12 +1632,10 @@ export class MessagePayload {
|
||||
options: string | MessageOptions | WebhookMessageOptions,
|
||||
extra?: MessageOptions | WebhookMessageOptions,
|
||||
): MessagePayload;
|
||||
public static resolveFile(
|
||||
fileLike: BufferResolvable | Stream | FileOptions | MessageAttachment,
|
||||
): Promise<HTTPAttachmentData>;
|
||||
public static resolveFile(fileLike: BufferResolvable | Stream | FileOptions | MessageAttachment): Promise<RawFile>;
|
||||
|
||||
public makeContent(): string | undefined;
|
||||
public resolveData(): this;
|
||||
public resolveBody(): this;
|
||||
public resolveFiles(): Promise<this>;
|
||||
}
|
||||
|
||||
@@ -2355,7 +2290,8 @@ export class Util extends null {
|
||||
position: number,
|
||||
relative: boolean,
|
||||
sorted: Collection<Snowflake, T>,
|
||||
route: unknown,
|
||||
client: Client,
|
||||
route: string,
|
||||
reason?: string,
|
||||
): Promise<{ id: Snowflake; position: number }[]>;
|
||||
public static splitMessage(text: string, options?: SplitOptions): string[];
|
||||
@@ -2636,29 +2572,6 @@ export const Constants: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
UserAgent: string;
|
||||
Endpoints: {
|
||||
botGateway: string;
|
||||
invite: (root: string, code: string, eventId?: Snowflake) => string;
|
||||
scheduledEvent: (root: string, guildId: Snowflake, eventId: Snowflake) => string;
|
||||
CDN: (root: string) => {
|
||||
Emoji: (emojiId: Snowflake, format: 'gif' | 'png') => string;
|
||||
Asset: (name: string) => string;
|
||||
DefaultAvatar: (discriminator: number) => string;
|
||||
Avatar: (userId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
Banner: (id: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
GuildMemberAvatar: (guildId: Snowflake, memberId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
Icon: (guildId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
AppIcon: (appId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
AppAsset: (appId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
StickerPackBanner: (bannerId: Snowflake, options: ImageURLOptions) => string;
|
||||
GDMIcon: (channelId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
Splash: (guildId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
DiscoverySplash: (guildId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
TeamIcon: (teamId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
Sticker: (stickerId: Snowflake, format: StickerFormatType) => string;
|
||||
RoleIcon: (roleId: Snowflake, hash: string, options: ImageURLOptions) => string;
|
||||
};
|
||||
};
|
||||
WSCodes: {
|
||||
1000: 'WS_CLOSE_REQUESTED';
|
||||
4004: 'TOKEN_INVALID';
|
||||
@@ -2726,7 +2639,7 @@ export class ApplicationCommandManager<
|
||||
PermissionsGuildType,
|
||||
null
|
||||
>;
|
||||
private commandPath({ id, guildId }: { id?: Snowflake; guildId?: Snowflake }): unknown;
|
||||
private commandPath({ id, guildId }: { id?: Snowflake; guildId?: Snowflake }): string;
|
||||
public create(command: ApplicationCommandDataResolvable, guildId?: Snowflake): Promise<ApplicationCommandScope>;
|
||||
public delete(command: ApplicationCommandResolvable, guildId?: Snowflake): Promise<ApplicationCommandScope | null>;
|
||||
public edit(
|
||||
@@ -2797,7 +2710,7 @@ export class ApplicationCommandPermissionsManager<
|
||||
fullPermissions: GuildApplicationCommandPermissionData[];
|
||||
},
|
||||
): Promise<Collection<Snowflake, ApplicationCommandPermissions[]>>;
|
||||
private permissionsPath(guildId: Snowflake, commandId?: Snowflake): unknown;
|
||||
private permissionsPath(guildId: Snowflake, commandId?: Snowflake): string;
|
||||
}
|
||||
|
||||
export class BaseGuildEmojiManager extends CachedManager<Snowflake, GuildEmoji, EmojiResolvable> {
|
||||
@@ -3208,24 +3121,12 @@ export interface AddGuildMemberOptions {
|
||||
fetchWhenExisting?: boolean;
|
||||
}
|
||||
|
||||
export type AllowedImageFormat = 'webp' | 'png' | 'jpg' | 'jpeg';
|
||||
|
||||
export type AllowedImageSize = 16 | 32 | 56 | 64 | 96 | 128 | 256 | 300 | 512 | 600 | 1024 | 2048 | 4096;
|
||||
|
||||
export type AllowedPartial = User | Channel | GuildMember | Message | MessageReaction;
|
||||
|
||||
export type AllowedThreadTypeForNewsChannel = ChannelType.GuildNewsThread;
|
||||
|
||||
export type AllowedThreadTypeForTextChannel = ChannelType.GuildPublicThread | ChannelType.GuildPrivateThread;
|
||||
|
||||
export interface APIRequest {
|
||||
method: 'get' | 'post' | 'delete' | 'patch' | 'put';
|
||||
options: unknown;
|
||||
path: string;
|
||||
retries: number;
|
||||
route: string;
|
||||
}
|
||||
|
||||
export interface BaseApplicationCommandData {
|
||||
name: string;
|
||||
defaultPermission?: boolean;
|
||||
@@ -3554,15 +3455,7 @@ export interface ChannelWebhookCreateOptions {
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface BaseClientEvents {
|
||||
apiResponse: [request: APIRequest, response: Response];
|
||||
apiRequest: [request: APIRequest];
|
||||
debug: [message: string];
|
||||
rateLimit: [rateLimitData: RateLimitData];
|
||||
invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData];
|
||||
}
|
||||
|
||||
export interface ClientEvents extends BaseClientEvents {
|
||||
export interface ClientEvents {
|
||||
cacheSweep: [message: string];
|
||||
channelCreate: [channel: NonThreadGuildBasedChannel];
|
||||
channelDelete: [channel: DMChannel | NonThreadGuildBasedChannel];
|
||||
@@ -3571,6 +3464,7 @@ export interface ClientEvents extends BaseClientEvents {
|
||||
oldChannel: DMChannel | NonThreadGuildBasedChannel,
|
||||
newChannel: DMChannel | NonThreadGuildBasedChannel,
|
||||
];
|
||||
debug: [message: string];
|
||||
warn: [message: string];
|
||||
emojiCreate: [emoji: GuildEmoji];
|
||||
emojiDelete: [emoji: GuildEmoji];
|
||||
@@ -3652,22 +3546,14 @@ export interface ClientOptions {
|
||||
shardCount?: number;
|
||||
makeCache?: CacheFactory;
|
||||
allowedMentions?: MessageMentionOptions;
|
||||
invalidRequestWarningInterval?: number;
|
||||
partials?: PartialTypes[];
|
||||
restTimeOffset?: number;
|
||||
restRequestTimeout?: number;
|
||||
restGlobalRateLimit?: number;
|
||||
restSweepInterval?: number;
|
||||
retryLimit?: number;
|
||||
failIfNotExists?: boolean;
|
||||
userAgentSuffix?: string[];
|
||||
presence?: PresenceData;
|
||||
intents: BitFieldResolvable<IntentsString, number>;
|
||||
waitGuildTimeout?: number;
|
||||
sweepers?: SweeperOptions;
|
||||
ws?: WebSocketOptions;
|
||||
http?: HTTPOptions;
|
||||
rejectOnRateLimit?: string[] | ((data: RateLimitData) => boolean | Promise<boolean>);
|
||||
rest?: RESTOptions;
|
||||
}
|
||||
|
||||
export type ClientPresenceStatus = 'online' | 'idle' | 'dnd';
|
||||
@@ -3796,10 +3682,6 @@ export interface ConstantsColors {
|
||||
}
|
||||
|
||||
export interface ConstantsEvents {
|
||||
RATE_LIMIT: 'rateLimit';
|
||||
INVALID_REQUEST_WARNING: 'invalidRequestWarning';
|
||||
API_RESPONSE: 'apiResponse';
|
||||
API_REQUEST: 'apiRequest';
|
||||
CLIENT_READY: 'ready';
|
||||
GUILD_CREATE: 'guildCreate';
|
||||
GUILD_DELETE: 'guildDelete';
|
||||
@@ -4537,35 +4419,6 @@ export type GuildVoiceChannelResolvable = VoiceBasedChannel | Snowflake;
|
||||
|
||||
export type HexColorString = `#${string}`;
|
||||
|
||||
export interface HTTPAttachmentData {
|
||||
attachment: string | Buffer | Stream;
|
||||
name: string;
|
||||
file: Buffer | Stream;
|
||||
}
|
||||
|
||||
export interface HTTPErrorData {
|
||||
json: unknown;
|
||||
files: HTTPAttachmentData[];
|
||||
}
|
||||
|
||||
export interface HTTPOptions {
|
||||
agent?: Omit<AgentOptions, 'keepAlive'>;
|
||||
api?: string;
|
||||
version?: number;
|
||||
host?: string;
|
||||
cdn?: string;
|
||||
invite?: string;
|
||||
template?: string;
|
||||
headers?: Record<string, string>;
|
||||
scheduledEvent?: string;
|
||||
}
|
||||
|
||||
export interface ImageURLOptions {
|
||||
format?: AllowedImageFormat;
|
||||
forceStatic?: boolean;
|
||||
size?: AllowedImageSize;
|
||||
}
|
||||
|
||||
export interface IntegrationAccount {
|
||||
id: string | Snowflake;
|
||||
name: string;
|
||||
@@ -5004,20 +4857,6 @@ export type PresenceStatusData = ClientPresenceStatus | 'invisible';
|
||||
|
||||
export type PresenceStatus = PresenceStatusData | 'offline';
|
||||
|
||||
export interface RateLimitData {
|
||||
timeout: number;
|
||||
limit: number;
|
||||
method: string;
|
||||
path: string;
|
||||
route: string;
|
||||
global: boolean;
|
||||
}
|
||||
|
||||
export interface InvalidRequestWarningData {
|
||||
count: number;
|
||||
remainingTime: number;
|
||||
}
|
||||
|
||||
export interface ReactionCollectorOptions extends CollectorOptions<[MessageReaction, User]> {
|
||||
max?: number;
|
||||
maxEmojis?: number;
|
||||
@@ -5270,10 +5109,7 @@ export interface WebhookClientDataURL {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type WebhookClientOptions = Pick<
|
||||
ClientOptions,
|
||||
'allowedMentions' | 'restTimeOffset' | 'restRequestTimeout' | 'retryLimit' | 'http'
|
||||
>;
|
||||
export type WebhookClientOptions = Pick<ClientOptions, 'allowedMentions' | 'rest'>;
|
||||
|
||||
export interface WebhookEditData {
|
||||
name?: string;
|
||||
@@ -5455,3 +5291,4 @@ export {
|
||||
SelectMenuOption,
|
||||
ActionRowComponent,
|
||||
} from '@discordjs/builders';
|
||||
export { DiscordAPIError, HTTPError, RateLimitError } from '@discordjs/rest';
|
||||
|
||||
Reference in New Issue
Block a user