This commit is contained in:
Lukian LEIZOUR 2023-01-29 00:57:47 +01:00
parent a15c733e45
commit 2a5130cbda
2838 changed files with 288613 additions and 0 deletions

865
node_modules/@discordjs/rest/dist/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,865 @@
import { Agent, Dispatcher, request, BodyInit } from 'undici';
import { Buffer } from 'node:buffer';
import { EventEmitter } from 'node:events';
import { URLSearchParams } from 'node:url';
import { Collection } from '@discordjs/collection';
declare const DefaultUserAgent = "DiscordBot (https://discord.js.org, [VI]{{inject}}[/VI])";
declare const DefaultRestOptions: {
readonly agent: Agent;
readonly api: "https://discord.com/api";
readonly authPrefix: "Bot";
readonly cdn: "https://cdn.discordapp.com";
readonly headers: {};
readonly invalidRequestWarningInterval: 0;
readonly globalRequestsPerSecond: 50;
readonly offset: 50;
readonly rejectOnRateLimit: null;
readonly retries: 3;
readonly timeout: 15000;
readonly userAgentAppendix: `Node.js ${string}`;
readonly version: "10";
readonly hashSweepInterval: 14400000;
readonly hashLifetime: 86400000;
readonly handlerSweepInterval: 3600000;
};
/**
* The events that the REST manager emits
*/
declare const enum RESTEvents {
Debug = "restDebug",
HandlerSweep = "handlerSweep",
HashSweep = "hashSweep",
InvalidRequestWarning = "invalidRequestWarning",
RateLimited = "rateLimited",
Response = "response"
}
declare const ALLOWED_EXTENSIONS: readonly ["webp", "png", "jpg", "jpeg", "gif"];
declare const ALLOWED_STICKER_EXTENSIONS: readonly ["png", "json"];
declare const ALLOWED_SIZES: readonly [16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
type ImageExtension = typeof ALLOWED_EXTENSIONS[number];
type StickerExtension = typeof ALLOWED_STICKER_EXTENSIONS[number];
type ImageSize = typeof ALLOWED_SIZES[number];
/**
* The options used for image URLs
*/
interface BaseImageURLOptions {
/**
* The extension to use for the image URL
*
* @defaultValue `'webp'`
*/
extension?: ImageExtension;
/**
* The size specified in the image URL
*/
size?: ImageSize;
}
/**
* The options used for image URLs with animated content
*/
interface ImageURLOptions extends BaseImageURLOptions {
/**
* Whether or not to prefer the static version of an image asset.
*/
forceStatic?: boolean;
}
/**
* The options to use when making a CDN URL
*/
interface MakeURLOptions {
/**
* The allowed extensions that can be used
*/
allowedExtensions?: readonly string[];
/**
* The extension to use for the image URL
*
* @defaultValue `'webp'`
*/
extension?: string | undefined;
/**
* The size specified in the image URL
*/
size?: ImageSize;
}
/**
* The CDN link builder
*/
declare class CDN {
private readonly base;
constructor(base?: string);
/**
* Generates an app asset URL for a client's asset.
*
* @param clientId - The client id that has the asset
* @param assetHash - The hash provided by Discord for this asset
* @param options - Optional options for the asset
*/
appAsset(clientId: string, assetHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates an app icon URL for a client's icon.
*
* @param clientId - The client id that has the icon
* @param iconHash - The hash provided by Discord for this icon
* @param options - Optional options for the icon
*/
appIcon(clientId: string, iconHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates an avatar URL, e.g. for a user or a webhook.
*
* @param id - The id that has the icon
* @param avatarHash - The hash provided by Discord for this avatar
* @param options - Optional options for the avatar
*/
avatar(id: string, avatarHash: string, options?: Readonly<ImageURLOptions>): string;
/**
* Generates a banner URL, e.g. for a user or a guild.
*
* @param id - The id that has the banner splash
* @param bannerHash - The hash provided by Discord for this banner
* @param options - Optional options for the banner
*/
banner(id: string, bannerHash: string, options?: Readonly<ImageURLOptions>): string;
/**
* Generates an icon URL for a channel, e.g. a group DM.
*
* @param channelId - The channel id that has the icon
* @param iconHash - The hash provided by Discord for this channel
* @param options - Optional options for the icon
*/
channelIcon(channelId: string, iconHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates the default avatar URL for a discriminator.
*
* @param discriminator - The discriminator modulo 5
*/
defaultAvatar(discriminator: number): string;
/**
* Generates a discovery splash URL for a guild's discovery splash.
*
* @param guildId - The guild id that has the discovery splash
* @param splashHash - The hash provided by Discord for this splash
* @param options - Optional options for the splash
*/
discoverySplash(guildId: string, splashHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates an emoji's URL for an emoji.
*
* @param emojiId - The emoji id
* @param extension - The extension of the emoji
*/
emoji(emojiId: string, extension?: ImageExtension): string;
/**
* Generates a guild member avatar URL.
*
* @param guildId - The id of the guild
* @param userId - The id of the user
* @param avatarHash - The hash provided by Discord for this avatar
* @param options - Optional options for the avatar
*/
guildMemberAvatar(guildId: string, userId: string, avatarHash: string, options?: Readonly<ImageURLOptions>): string;
/**
* Generates a guild member banner URL.
*
* @param guildId - The id of the guild
* @param userId - The id of the user
* @param bannerHash - The hash provided by Discord for this banner
* @param options - Optional options for the banner
*/
guildMemberBanner(guildId: string, userId: string, bannerHash: string, options?: Readonly<ImageURLOptions>): string;
/**
* Generates an icon URL, e.g. for a guild.
*
* @param id - The id that has the icon splash
* @param iconHash - The hash provided by Discord for this icon
* @param options - Optional options for the icon
*/
icon(id: string, iconHash: string, options?: Readonly<ImageURLOptions>): string;
/**
* Generates a URL for the icon of a role
*
* @param roleId - The id of the role that has the icon
* @param roleIconHash - The hash provided by Discord for this role icon
* @param options - Optional options for the role icon
*/
roleIcon(roleId: string, roleIconHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates a guild invite splash URL for a guild's invite splash.
*
* @param guildId - The guild id that has the invite splash
* @param splashHash - The hash provided by Discord for this splash
* @param options - Optional options for the splash
*/
splash(guildId: string, splashHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates a sticker URL.
*
* @param stickerId - The sticker id
* @param extension - The extension of the sticker
*/
sticker(stickerId: string, extension?: StickerExtension): string;
/**
* Generates a sticker pack banner URL.
*
* @param bannerId - The banner id
* @param options - Optional options for the banner
*/
stickerPackBanner(bannerId: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates a team icon URL for a team's icon.
*
* @param teamId - The team id that has the icon
* @param iconHash - The hash provided by Discord for this icon
* @param options - Optional options for the icon
*/
teamIcon(teamId: string, iconHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Generates a cover image for a guild scheduled event.
*
* @param scheduledEventId - The scheduled event id
* @param coverHash - The hash provided by discord for this cover image
* @param options - Optional options for the cover image
*/
guildScheduledEventCover(scheduledEventId: string, coverHash: string, options?: Readonly<BaseImageURLOptions>): string;
/**
* Constructs the URL for the resource, checking whether or not `hash` starts with `a_` if `dynamic` is set to `true`.
*
* @param route - The base cdn route
* @param hash - The hash provided by Discord for this icon
* @param options - Optional options for the link
*/
private dynamicMakeURL;
/**
* Constructs the URL for the resource
*
* @param route - The base cdn route
* @param options - The extension/size options for the link
*/
private makeURL;
}
interface IHandler {
/**
* The unique id of the handler
*/
readonly id: string;
/**
* If the bucket is currently inactive (no pending requests)
*/
get inactive(): boolean;
/**
* Queues a request to be sent
*
* @param routeId - The generalized api route with literal ids for major parameters
* @param url - The url to do the request on
* @param options - All the information needed to make a request
* @param requestData - Extra data from the user's request needed for errors and additional processing
*/
queueRequest(routeId: RouteData, url: string, options: RequestOptions, requestData: HandlerRequestData): Promise<Dispatcher.ResponseData>;
}
/**
* Options to be passed when creating the REST instance
*/
interface RESTOptions {
/**
* The agent to set globally
*/
agent: Dispatcher;
/**
* The base api path, without version
*
* @defaultValue `'https://discord.com/api'`
*/
api: string;
/**
* The authorization prefix to use for requests, useful if you want to use
* bearer tokens
*
* @defaultValue `'Bot'`
*/
authPrefix: 'Bearer' | 'Bot';
/**
* The cdn path
*
* @defaultValue 'https://cdn.discordapp.com'
*/
cdn: string;
/**
* How many requests to allow sending per second (Infinity for unlimited, 50 for the standard global limit used by Discord)
*
* @defaultValue `50`
*/
globalRequestsPerSecond: number;
/**
* The amount of time in milliseconds that passes between each hash sweep. (defaults to 1h)
*
* @defaultValue `3_600_000`
*/
handlerSweepInterval: number;
/**
* The maximum amount of time a hash can exist in milliseconds without being hit with a request (defaults to 24h)
*
* @defaultValue `86_400_000`
*/
hashLifetime: number;
/**
* The amount of time in milliseconds that passes between each hash sweep. (defaults to 4h)
*
* @defaultValue `14_400_000`
*/
hashSweepInterval: number;
/**
* Additional headers to send for all API requests
*
* @defaultValue `{}`
*/
headers: Record<string, string>;
/**
* 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.
*
* @defaultValue `0`
*/
invalidRequestWarningInterval: number;
/**
* The extra offset to add to rate limits in milliseconds
*
* @defaultValue `50`
*/
offset: number;
/**
* Determines how rate limiting and pre-emptive throttling should be handled.
* When an array of strings, each element is treated as a prefix for the request route
* (e.g. `/channels` to match any route starting with `/channels` such as `/channels/:id/messages`)
* for which to throw {@link RateLimitError}s. All other request routes will be queued normally
*
* @defaultValue `null`
*/
rejectOnRateLimit: RateLimitQueueFilter | string[] | null;
/**
* The number of retries for errors with the 500 code, or errors
* that timeout
*
* @defaultValue `3`
*/
retries: number;
/**
* The time to wait in milliseconds before a request is aborted
*
* @defaultValue `15_000`
*/
timeout: number;
/**
* Extra information to add to the user agent
*
* @defaultValue `Node.js ${process.version}`
*/
userAgentAppendix: string;
/**
* The version of the API to use
*
* @defaultValue `'10'`
*/
version: string;
}
/**
* Data emitted on `RESTEvents.RateLimited`
*/
interface RateLimitData {
/**
* Whether the rate limit that was reached was the global limit
*/
global: boolean;
/**
* The bucket hash for this request
*/
hash: string;
/**
* The amount of requests we can perform before locking requests
*/
limit: number;
/**
* The major parameter of the route
*
* For example, in `/channels/x`, this will be `x`.
* If there is no major parameter (e.g: `/bot/gateway`) this will be `global`.
*/
majorParameter: string;
/**
* The HTTP method being performed
*/
method: string;
/**
* The route being hit in this request
*/
route: string;
/**
* The time, in milliseconds, until the request-lock is reset
*/
timeToReset: number;
/**
* The full URL for this request
*/
url: string;
}
/**
* A function that determines whether the rate limit hit should throw an Error
*/
type RateLimitQueueFilter = (rateLimitData: RateLimitData) => Promise<boolean> | boolean;
interface APIRequest {
/**
* The data that was used to form the body of this request
*/
data: HandlerRequestData;
/**
* The HTTP method used in this request
*/
method: string;
/**
* Additional HTTP options for this request
*/
options: RequestOptions;
/**
* The full path used to make the request
*/
path: RouteLike;
/**
* The number of times this request has been attempted
*/
retries: number;
/**
* The API route identifying the ratelimit for this request
*/
route: string;
}
interface InvalidRequestWarningData {
/**
* Number of invalid requests that have been made in the window
*/
count: number;
/**
* Time in milliseconds remaining before the count resets
*/
remainingTime: number;
}
interface RestEvents {
handlerSweep: [sweptHandlers: Collection<string, IHandler>];
hashSweep: [sweptHashes: Collection<string, HashData>];
invalidRequestWarning: [invalidRequestInfo: InvalidRequestWarningData];
newListener: [name: string, listener: (...args: any) => void];
rateLimited: [rateLimitInfo: RateLimitData];
removeListener: [name: string, listener: (...args: any) => void];
response: [request: APIRequest, response: Dispatcher.ResponseData];
restDebug: [info: string];
}
type RequestOptions = Exclude<Parameters<typeof request>[1], undefined>;
interface REST {
emit: (<K extends keyof RestEvents>(event: K, ...args: RestEvents[K]) => boolean) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, ...args: any[]) => boolean);
off: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
on: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
once: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
removeAllListeners: (<K extends keyof RestEvents>(event?: K) => this) & (<S extends string | symbol>(event?: Exclude<S, keyof RestEvents>) => this);
}
declare class REST extends EventEmitter {
readonly cdn: CDN;
readonly requestManager: RequestManager;
constructor(options?: Partial<RESTOptions>);
/**
* Gets the agent set for this instance
*/
getAgent(): Dispatcher | null;
/**
* Sets the default agent to use for requests performed by this instance
*
* @param agent - Sets the agent to use
*/
setAgent(agent: Dispatcher): this;
/**
* Sets the authorization token that should be used for requests
*
* @param token - The authorization token to use
*/
setToken(token: string): this;
/**
* Runs a get request from the api
*
* @param fullRoute - The full route to query
* @param options - Optional request options
*/
get(fullRoute: RouteLike, options?: RequestData): Promise<unknown>;
/**
* Runs a delete request from the api
*
* @param fullRoute - The full route to query
* @param options - Optional request options
*/
delete(fullRoute: RouteLike, options?: RequestData): Promise<unknown>;
/**
* Runs a post request from the api
*
* @param fullRoute - The full route to query
* @param options - Optional request options
*/
post(fullRoute: RouteLike, options?: RequestData): Promise<unknown>;
/**
* Runs a put request from the api
*
* @param fullRoute - The full route to query
* @param options - Optional request options
*/
put(fullRoute: RouteLike, options?: RequestData): Promise<unknown>;
/**
* Runs a patch request from the api
*
* @param fullRoute - The full route to query
* @param options - Optional request options
*/
patch(fullRoute: RouteLike, options?: RequestData): Promise<unknown>;
/**
* Runs a request from the api
*
* @param options - Request options
*/
request(options: InternalRequest): Promise<unknown>;
/**
* Runs a request from the API, yielding the raw Response object
*
* @param options - Request options
*/
raw(options: InternalRequest): Promise<Dispatcher.ResponseData>;
}
/**
* Represents a file to be added to the request
*/
interface RawFile {
/**
* Content-Type of the file
*/
contentType?: string;
/**
* The actual data for the file
*/
data: Buffer | boolean | number | string;
/**
* An explicit key to use for key of the formdata field for this file.
* When not provided, the index of the file in the files array is used in the form `files[${index}]`.
* If you wish to alter the placeholder snowflake, you must provide this property in the same form (`files[${placeholder}]`)
*/
key?: string;
/**
* The name of the file
*/
name: string;
}
/**
* Represents possible data to be given to an endpoint
*/
interface RequestData {
/**
* Whether to append JSON data to form data instead of `payload_json` when sending files
*/
appendToFormData?: boolean;
/**
* If this request needs the `Authorization` header
*
* @defaultValue `true`
*/
auth?: boolean;
/**
* The authorization prefix to use for this request, useful if you use this with bearer tokens
*
* @defaultValue `'Bot'`
*/
authPrefix?: 'Bearer' | 'Bot';
/**
* The body to send to this request.
* If providing as BodyInit, set `passThroughBody: true`
*/
body?: BodyInit | unknown;
/**
* The {@link https://undici.nodejs.org/#/docs/api/Agent | Agent} to use for the request.
*/
dispatcher?: Agent;
/**
* Files to be attached to this request
*/
files?: RawFile[] | undefined;
/**
* Additional headers to add to this request
*/
headers?: Record<string, string>;
/**
* Whether to pass-through the body property directly to `fetch()`.
* <warn>This only applies when files is NOT present</warn>
*/
passThroughBody?: boolean;
/**
* Query string parameters to append to the called endpoint
*/
query?: URLSearchParams;
/**
* Reason to show in the audit logs
*/
reason?: string | undefined;
/**
* The signal to abort the queue entry or the REST call, where applicable
*/
signal?: AbortSignal | undefined;
/**
* If this request should be versioned
*
* @defaultValue `true`
*/
versioned?: boolean;
}
/**
* Possible headers for an API call
*/
interface RequestHeaders {
Authorization?: string;
'User-Agent': string;
'X-Audit-Log-Reason'?: string;
}
/**
* Possible API methods to be used when doing requests
*/
declare const enum RequestMethod {
Delete = "DELETE",
Get = "GET",
Patch = "PATCH",
Post = "POST",
Put = "PUT"
}
type RouteLike = `/${string}`;
/**
* Internal request options
*
* @internal
*/
interface InternalRequest extends RequestData {
fullRoute: RouteLike;
method: RequestMethod;
}
type HandlerRequestData = Pick<InternalRequest, 'auth' | 'body' | 'files' | 'signal'>;
/**
* Parsed route data for an endpoint
*
* @internal
*/
interface RouteData {
bucketRoute: string;
majorParameter: string;
original: RouteLike;
}
/**
* Represents a hash and its associated fields
*
* @internal
*/
interface HashData {
lastAccess: number;
value: string;
}
interface RequestManager {
emit: (<K extends keyof RestEvents>(event: K, ...args: RestEvents[K]) => boolean) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, ...args: any[]) => boolean);
off: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
on: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
once: (<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void) => this) & (<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void) => this);
removeAllListeners: (<K extends keyof RestEvents>(event?: K) => this) & (<S extends string | symbol>(event?: Exclude<S, keyof RestEvents>) => this);
}
/**
* Represents the class that manages handlers for endpoints
*/
declare class RequestManager extends EventEmitter {
#private;
/**
* The {@link https://undici.nodejs.org/#/docs/api/Agent | Agent} for all requests
* performed by this manager.
*/
agent: Dispatcher | null;
/**
* The number of requests remaining in the global bucket
*/
globalRemaining: number;
/**
* The promise used to wait out the global rate limit
*/
globalDelay: Promise<void> | null;
/**
* The timestamp at which the global bucket resets
*/
globalReset: number;
/**
* API bucket hashes that are cached from provided routes
*/
readonly hashes: Collection<string, HashData>;
/**
* Request handlers created from the bucket hash and the major parameters
*/
readonly handlers: Collection<string, IHandler>;
private hashTimer;
private handlerTimer;
readonly options: RESTOptions;
constructor(options: Partial<RESTOptions>);
private setupSweepers;
/**
* Sets the default agent to use for requests performed by this manager
*
* @param agent - The agent to use
*/
setAgent(agent: Dispatcher): this;
/**
* Sets the authorization token that should be used for requests
*
* @param token - The authorization token to use
*/
setToken(token: string): this;
/**
* Queues a request to be sent
*
* @param request - All the information needed to make a request
* @returns The response from the api request
*/
queueRequest(request: InternalRequest): Promise<Dispatcher.ResponseData>;
/**
* Creates a new rate limit handler from a hash, based on the hash and the major parameter
*
* @param hash - The hash for the route
* @param majorParameter - The major parameter for this handler
* @internal
*/
private createHandler;
/**
* Formats the request data to a usable format for fetch
*
* @param request - The request data
*/
private resolveRequest;
/**
* Stops the hash sweeping interval
*/
clearHashSweeper(): void;
/**
* Stops the request handler sweeping interval
*/
clearHandlerSweeper(): void;
/**
* Generates route data for an endpoint:method
*
* @param endpoint - The raw endpoint to generalize
* @param method - The HTTP method this endpoint is called without
* @internal
*/
private static generateRouteData;
}
interface DiscordErrorFieldInformation {
code: string;
message: string;
}
interface DiscordErrorGroupWrapper {
_errors: DiscordError[];
}
type DiscordError = DiscordErrorFieldInformation | DiscordErrorGroupWrapper | string | {
[k: string]: DiscordError;
};
interface DiscordErrorData {
code: number;
errors?: DiscordError;
message: string;
}
interface OAuthErrorData {
error: string;
error_description?: string;
}
interface RequestBody {
files: RawFile[] | undefined;
json: unknown | undefined;
}
/**
* Represents an API error returned by Discord
*/
declare class DiscordAPIError extends Error {
rawError: DiscordErrorData | OAuthErrorData;
code: number | string;
status: number;
method: string;
url: string;
requestBody: RequestBody;
/**
* @param rawError - The error reported by Discord
* @param code - The error code reported by Discord
* @param status - The status code of the response
* @param method - The method of the request that erred
* @param url - The url of the request that erred
* @param bodyData - The unparsed data for the request that errored
*/
constructor(rawError: DiscordErrorData | OAuthErrorData, code: number | string, status: number, method: string, url: string, bodyData: Pick<InternalRequest, 'body' | 'files'>);
/**
* The name of the error
*/
get name(): string;
private static getMessage;
private static flattenDiscordError;
}
/**
* Represents a HTTP error
*/
declare class HTTPError extends Error {
status: number;
method: string;
url: string;
requestBody: RequestBody;
name: string;
/**
* @param status - The status code of the response
* @param method - The method of the request that erred
* @param url - The url of the request that erred
* @param bodyData - The unparsed data for the request that errored
*/
constructor(status: number, method: string, url: string, bodyData: Pick<InternalRequest, 'body' | 'files'>);
}
declare class RateLimitError extends Error implements RateLimitData {
timeToReset: number;
limit: number;
method: string;
hash: string;
url: string;
route: string;
majorParameter: string;
global: boolean;
constructor({ timeToReset, limit, method, hash, url, route, majorParameter, global }: RateLimitData);
/**
* The name of the error
*/
get name(): string;
}
/**
* Creates and populates an URLSearchParams instance from an object, stripping
* out null and undefined values, while also coercing non-strings to strings.
*
* @param options - The options to use
* @returns A populated URLSearchParams instance
*/
declare function makeURLSearchParams<T extends object>(options?: Readonly<T>): URLSearchParams;
/**
* Converts the response to usable data
*
* @param res - The fetch response
*/
declare function parseResponse(res: Dispatcher.ResponseData): Promise<unknown>;
/**
* The {@link https://github.com/discordjs/discord.js/blob/main/packages/rest/#readme | @discordjs/rest} version
* that you are currently using.
*/
declare const version: string;
export { ALLOWED_EXTENSIONS, ALLOWED_SIZES, ALLOWED_STICKER_EXTENSIONS, APIRequest, BaseImageURLOptions, CDN, DefaultRestOptions, DefaultUserAgent, DiscordAPIError, DiscordErrorData, HTTPError, HandlerRequestData, HashData, ImageExtension, ImageSize, ImageURLOptions, InternalRequest, InvalidRequestWarningData, MakeURLOptions, OAuthErrorData, REST, RESTEvents, RESTOptions, RateLimitData, RateLimitError, RateLimitQueueFilter, RawFile, RequestBody, RequestData, RequestHeaders, RequestManager, RequestMethod, RequestOptions, RestEvents, RouteData, RouteLike, StickerExtension, makeURLSearchParams, parseResponse, version };

940
node_modules/@discordjs/rest/dist/index.js generated vendored Normal file
View file

@ -0,0 +1,940 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ALLOWED_EXTENSIONS: () => ALLOWED_EXTENSIONS,
ALLOWED_SIZES: () => ALLOWED_SIZES,
ALLOWED_STICKER_EXTENSIONS: () => ALLOWED_STICKER_EXTENSIONS,
CDN: () => CDN,
DefaultRestOptions: () => DefaultRestOptions,
DefaultUserAgent: () => DefaultUserAgent,
DiscordAPIError: () => DiscordAPIError,
HTTPError: () => HTTPError,
REST: () => REST,
RESTEvents: () => RESTEvents,
RateLimitError: () => RateLimitError,
RequestManager: () => RequestManager,
RequestMethod: () => RequestMethod,
makeURLSearchParams: () => makeURLSearchParams,
parseResponse: () => parseResponse,
version: () => version
});
module.exports = __toCommonJS(src_exports);
// src/lib/CDN.ts
var import_node_url = require("url");
// src/lib/utils/constants.ts
var import_node_process = __toESM(require("process"));
var import_v10 = require("discord-api-types/v10");
var import_undici = require("undici");
var DefaultUserAgent = `DiscordBot (https://discord.js.org, 1.5.0)`;
var DefaultRestOptions = {
get agent() {
return new import_undici.Agent({
connect: {
timeout: 3e4
}
});
},
api: "https://discord.com/api",
authPrefix: "Bot",
cdn: "https://cdn.discordapp.com",
headers: {},
invalidRequestWarningInterval: 0,
globalRequestsPerSecond: 50,
offset: 50,
rejectOnRateLimit: null,
retries: 3,
timeout: 15e3,
userAgentAppendix: `Node.js ${import_node_process.default.version}`,
version: import_v10.APIVersion,
hashSweepInterval: 144e5,
hashLifetime: 864e5,
handlerSweepInterval: 36e5
};
var RESTEvents = /* @__PURE__ */ ((RESTEvents2) => {
RESTEvents2["Debug"] = "restDebug";
RESTEvents2["HandlerSweep"] = "handlerSweep";
RESTEvents2["HashSweep"] = "hashSweep";
RESTEvents2["InvalidRequestWarning"] = "invalidRequestWarning";
RESTEvents2["RateLimited"] = "rateLimited";
RESTEvents2["Response"] = "response";
return RESTEvents2;
})(RESTEvents || {});
var ALLOWED_EXTENSIONS = ["webp", "png", "jpg", "jpeg", "gif"];
var ALLOWED_STICKER_EXTENSIONS = ["png", "json"];
var ALLOWED_SIZES = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
// src/lib/CDN.ts
var CDN = class {
constructor(base = DefaultRestOptions.cdn) {
this.base = base;
}
appAsset(clientId, assetHash, options) {
return this.makeURL(`/app-assets/${clientId}/${assetHash}`, options);
}
appIcon(clientId, iconHash, options) {
return this.makeURL(`/app-icons/${clientId}/${iconHash}`, options);
}
avatar(id, avatarHash, options) {
return this.dynamicMakeURL(`/avatars/${id}/${avatarHash}`, avatarHash, options);
}
banner(id, bannerHash, options) {
return this.dynamicMakeURL(`/banners/${id}/${bannerHash}`, bannerHash, options);
}
channelIcon(channelId, iconHash, options) {
return this.makeURL(`/channel-icons/${channelId}/${iconHash}`, options);
}
defaultAvatar(discriminator) {
return this.makeURL(`/embed/avatars/${discriminator}`, { extension: "png" });
}
discoverySplash(guildId, splashHash, options) {
return this.makeURL(`/discovery-splashes/${guildId}/${splashHash}`, options);
}
emoji(emojiId, extension) {
return this.makeURL(`/emojis/${emojiId}`, { extension });
}
guildMemberAvatar(guildId, userId, avatarHash, options) {
return this.dynamicMakeURL(`/guilds/${guildId}/users/${userId}/avatars/${avatarHash}`, avatarHash, options);
}
guildMemberBanner(guildId, userId, bannerHash, options) {
return this.dynamicMakeURL(`/guilds/${guildId}/users/${userId}/banner`, bannerHash, options);
}
icon(id, iconHash, options) {
return this.dynamicMakeURL(`/icons/${id}/${iconHash}`, iconHash, options);
}
roleIcon(roleId, roleIconHash, options) {
return this.makeURL(`/role-icons/${roleId}/${roleIconHash}`, options);
}
splash(guildId, splashHash, options) {
return this.makeURL(`/splashes/${guildId}/${splashHash}`, options);
}
sticker(stickerId, extension) {
return this.makeURL(`/stickers/${stickerId}`, {
allowedExtensions: ALLOWED_STICKER_EXTENSIONS,
extension: extension ?? "png"
});
}
stickerPackBanner(bannerId, options) {
return this.makeURL(`/app-assets/710982414301790216/store/${bannerId}`, options);
}
teamIcon(teamId, iconHash, options) {
return this.makeURL(`/team-icons/${teamId}/${iconHash}`, options);
}
guildScheduledEventCover(scheduledEventId, coverHash, options) {
return this.makeURL(`/guild-events/${scheduledEventId}/${coverHash}`, options);
}
dynamicMakeURL(route, hash, { forceStatic = false, ...options } = {}) {
return this.makeURL(route, !forceStatic && hash.startsWith("a_") ? { ...options, extension: "gif" } : options);
}
makeURL(route, { allowedExtensions = ALLOWED_EXTENSIONS, extension = "webp", size } = {}) {
extension = String(extension).toLowerCase();
if (!allowedExtensions.includes(extension)) {
throw new RangeError(`Invalid extension provided: ${extension}
Must be one of: ${allowedExtensions.join(", ")}`);
}
if (size && !ALLOWED_SIZES.includes(size)) {
throw new RangeError(`Invalid size provided: ${size}
Must be one of: ${ALLOWED_SIZES.join(", ")}`);
}
const url = new import_node_url.URL(`${this.base}${route}.${extension}`);
if (size) {
url.searchParams.set("size", String(size));
}
return url.toString();
}
};
__name(CDN, "CDN");
// src/lib/errors/DiscordAPIError.ts
function isErrorGroupWrapper(error) {
return Reflect.has(error, "_errors");
}
__name(isErrorGroupWrapper, "isErrorGroupWrapper");
function isErrorResponse(error) {
return typeof Reflect.get(error, "message") === "string";
}
__name(isErrorResponse, "isErrorResponse");
var DiscordAPIError = class extends Error {
constructor(rawError, code, status, method, url, bodyData) {
super(DiscordAPIError.getMessage(rawError));
this.rawError = rawError;
this.code = code;
this.status = status;
this.method = method;
this.url = url;
this.requestBody = { files: bodyData.files, json: bodyData.body };
}
requestBody;
get name() {
return `${DiscordAPIError.name}[${this.code}]`;
}
static getMessage(error) {
let flattened = "";
if ("code" in error) {
if (error.errors) {
flattened = [...this.flattenDiscordError(error.errors)].join("\n");
}
return error.message && flattened ? `${error.message}
${flattened}` : error.message || flattened || "Unknown Error";
}
return error.error_description ?? "No Description";
}
static *flattenDiscordError(obj, key = "") {
if (isErrorResponse(obj)) {
return yield `${key.length ? `${key}[${obj.code}]` : `${obj.code}`}: ${obj.message}`.trim();
}
for (const [otherKey, val] of Object.entries(obj)) {
const nextKey = otherKey.startsWith("_") ? key : key ? Number.isNaN(Number(otherKey)) ? `${key}.${otherKey}` : `${key}[${otherKey}]` : otherKey;
if (typeof val === "string") {
yield val;
} else if (isErrorGroupWrapper(val)) {
for (const error of val._errors) {
yield* this.flattenDiscordError(error, nextKey);
}
} else {
yield* this.flattenDiscordError(val, nextKey);
}
}
}
};
__name(DiscordAPIError, "DiscordAPIError");
// src/lib/errors/HTTPError.ts
var import_node_http = require("http");
var HTTPError = class extends Error {
constructor(status, method, url, bodyData) {
super(import_node_http.STATUS_CODES[status]);
this.status = status;
this.method = method;
this.url = url;
this.requestBody = { files: bodyData.files, json: bodyData.body };
}
requestBody;
name = HTTPError.name;
};
__name(HTTPError, "HTTPError");
// src/lib/errors/RateLimitError.ts
var RateLimitError = class extends Error {
timeToReset;
limit;
method;
hash;
url;
route;
majorParameter;
global;
constructor({ timeToReset, limit, method, hash, url, route, majorParameter, global }) {
super();
this.timeToReset = timeToReset;
this.limit = limit;
this.method = method;
this.hash = hash;
this.url = url;
this.route = route;
this.majorParameter = majorParameter;
this.global = global;
}
get name() {
return `${RateLimitError.name}[${this.route}]`;
}
};
__name(RateLimitError, "RateLimitError");
// src/lib/RequestManager.ts
var import_node_buffer2 = require("buffer");
var import_node_events = require("events");
var import_node_timers2 = require("timers");
var import_collection = require("@discordjs/collection");
var import_util = require("@discordjs/util");
var import_snowflake = require("@sapphire/snowflake");
var import_undici4 = require("undici");
// src/lib/handlers/SequentialHandler.ts
var import_node_timers = require("timers");
var import_promises = require("timers/promises");
var import_async_queue = require("@sapphire/async-queue");
var import_undici3 = require("undici");
// src/lib/utils/utils.ts
var import_node_buffer = require("buffer");
var import_node_url2 = require("url");
var import_node_util = require("util");
var import_undici2 = require("undici");
function parseHeader(header) {
if (header === void 0 || typeof header === "string") {
return header;
}
return header.join(";");
}
__name(parseHeader, "parseHeader");
function serializeSearchParam(value) {
switch (typeof value) {
case "string":
return value;
case "number":
case "bigint":
case "boolean":
return value.toString();
case "object":
if (value === null)
return null;
if (value instanceof Date) {
return Number.isNaN(value.getTime()) ? null : value.toISOString();
}
if (typeof value.toString === "function" && value.toString !== Object.prototype.toString)
return value.toString();
return null;
default:
return null;
}
}
__name(serializeSearchParam, "serializeSearchParam");
function makeURLSearchParams(options) {
const params = new import_node_url2.URLSearchParams();
if (!options)
return params;
for (const [key, value] of Object.entries(options)) {
const serialized = serializeSearchParam(value);
if (serialized !== null)
params.append(key, serialized);
}
return params;
}
__name(makeURLSearchParams, "makeURLSearchParams");
async function parseResponse(res) {
const header = parseHeader(res.headers["content-type"]);
if (header?.startsWith("application/json")) {
return res.body.json();
}
return res.body.arrayBuffer();
}
__name(parseResponse, "parseResponse");
function hasSublimit(bucketRoute, body, method) {
if (bucketRoute === "/channels/:id") {
if (typeof body !== "object" || body === null)
return false;
if (method !== "PATCH" /* Patch */)
return false;
const castedBody = body;
return ["name", "topic"].some((key) => Reflect.has(castedBody, key));
}
return true;
}
__name(hasSublimit, "hasSublimit");
async function resolveBody(body) {
if (body == null) {
return null;
} else if (typeof body === "string") {
return body;
} else if (import_node_util.types.isUint8Array(body)) {
return body;
} else if (import_node_util.types.isArrayBuffer(body)) {
return new Uint8Array(body);
} else if (body instanceof import_node_url2.URLSearchParams) {
return body.toString();
} else if (body instanceof DataView) {
return new Uint8Array(body.buffer);
} else if (body instanceof import_node_buffer.Blob) {
return new Uint8Array(await body.arrayBuffer());
} else if (body instanceof import_undici2.FormData) {
return body;
} else if (body[Symbol.iterator]) {
const chunks = [...body];
const length = chunks.reduce((a, b) => a + b.length, 0);
const uint8 = new Uint8Array(length);
let lengthUsed = 0;
return chunks.reduce((a, b) => {
a.set(b, lengthUsed);
lengthUsed += b.length;
return a;
}, uint8);
} else if (body[Symbol.asyncIterator]) {
const chunks = [];
for await (const chunk of body) {
chunks.push(chunk);
}
return import_node_buffer.Buffer.concat(chunks);
}
throw new TypeError(`Unable to resolve body.`);
}
__name(resolveBody, "resolveBody");
function shouldRetry(error) {
if (error.name === "AbortError")
return true;
return "code" in error && error.code === "ECONNRESET" || error.message.includes("ECONNRESET");
}
__name(shouldRetry, "shouldRetry");
// src/lib/handlers/SequentialHandler.ts
var invalidCount = 0;
var invalidCountResetTime = null;
var SequentialHandler = class {
constructor(manager, hash, majorParameter) {
this.manager = manager;
this.hash = hash;
this.majorParameter = majorParameter;
this.id = `${hash}:${majorParameter}`;
}
id;
reset = -1;
remaining = 1;
limit = Number.POSITIVE_INFINITY;
#asyncQueue = new import_async_queue.AsyncQueue();
#sublimitedQueue = null;
#sublimitPromise = null;
#shiftSublimit = false;
get inactive() {
return this.#asyncQueue.remaining === 0 && (this.#sublimitedQueue === null || this.#sublimitedQueue.remaining === 0) && !this.limited;
}
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 timeToReset() {
return this.reset + this.manager.options.offset - Date.now();
}
debug(message) {
this.manager.emit("restDebug" /* Debug */, `[REST ${this.id}] ${message}`);
}
async globalDelayFor(time) {
await (0, import_promises.setTimeout)(time);
this.manager.globalDelay = null;
}
async onRateLimit(rateLimitData) {
const { options } = this.manager;
if (!options.rejectOnRateLimit)
return;
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 queueRequest(routeId, url, options, requestData) {
let queue = this.#asyncQueue;
let queueType = 0 /* Standard */;
if (this.#sublimitedQueue && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
queue = this.#sublimitedQueue;
queueType = 1 /* Sublimit */;
}
await queue.wait({ signal: requestData.signal });
if (queueType === 0 /* Standard */) {
if (this.#sublimitedQueue && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
queue = this.#sublimitedQueue;
const wait = queue.wait();
this.#asyncQueue.shift();
await wait;
} else if (this.#sublimitPromise) {
await this.#sublimitPromise.promise;
}
}
try {
return await this.runRequest(routeId, url, options, requestData);
} finally {
queue.shift();
if (this.#shiftSublimit) {
this.#shiftSublimit = false;
this.#sublimitedQueue?.shift();
}
if (this.#sublimitedQueue?.remaining === 0) {
this.#sublimitPromise?.resolve();
this.#sublimitedQueue = null;
}
}
}
async runRequest(routeId, url, options, requestData, retries = 0) {
while (this.limited) {
const isGlobal = this.globalLimited;
let limit2;
let timeout2;
let delay;
if (isGlobal) {
limit2 = this.manager.options.globalRequestsPerSecond;
timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
if (!this.manager.globalDelay) {
this.manager.globalDelay = this.globalDelayFor(timeout2);
}
delay = this.manager.globalDelay;
} else {
limit2 = this.limit;
timeout2 = this.timeToReset;
delay = (0, import_promises.setTimeout)(timeout2);
}
const rateLimitData = {
timeToReset: timeout2,
limit: limit2,
method: options.method ?? "get",
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal
};
this.manager.emit("rateLimited" /* RateLimited */, rateLimitData);
await this.onRateLimit(rateLimitData);
if (isGlobal) {
this.debug(`Global rate limit hit, blocking all requests for ${timeout2}ms`);
} else {
this.debug(`Waiting ${timeout2}ms for rate limit to pass`);
}
await delay;
}
if (!this.manager.globalReset || this.manager.globalReset < Date.now()) {
this.manager.globalReset = Date.now() + 1e3;
this.manager.globalRemaining = this.manager.options.globalRequestsPerSecond;
}
this.manager.globalRemaining--;
const method = options.method ?? "get";
const controller = new AbortController();
const timeout = (0, import_node_timers.setTimeout)(() => controller.abort(), this.manager.options.timeout).unref();
if (requestData.signal) {
const signal = requestData.signal;
if (signal.aborted)
controller.abort();
else
signal.addEventListener("abort", () => controller.abort());
}
let res;
try {
res = await (0, import_undici3.request)(url, { ...options, signal: controller.signal });
} catch (error) {
if (!(error instanceof Error))
throw error;
if (shouldRetry(error) && retries !== this.manager.options.retries) {
return await this.runRequest(routeId, url, options, requestData, ++retries);
}
throw error;
} finally {
(0, import_node_timers.clearTimeout)(timeout);
}
if (this.manager.listenerCount("response" /* Response */)) {
this.manager.emit(
"response" /* Response */,
{
method,
path: routeId.original,
route: routeId.bucketRoute,
options,
data: requestData,
retries
},
{ ...res }
);
}
const status = res.statusCode;
let retryAfter = 0;
const limit = parseHeader(res.headers["x-ratelimit-limit"]);
const remaining = parseHeader(res.headers["x-ratelimit-remaining"]);
const reset = parseHeader(res.headers["x-ratelimit-reset-after"]);
const hash = parseHeader(res.headers["x-ratelimit-bucket"]);
const retry = parseHeader(res.headers["retry-after"]);
this.limit = limit ? Number(limit) : Number.POSITIVE_INFINITY;
this.remaining = remaining ? Number(remaining) : 1;
this.reset = reset ? Number(reset) * 1e3 + Date.now() + this.manager.options.offset : Date.now();
if (retry)
retryAfter = Number(retry) * 1e3 + this.manager.options.offset;
if (hash && hash !== this.hash) {
this.debug(["Received bucket hash update", ` Old Hash : ${this.hash}`, ` New Hash : ${hash}`].join("\n"));
this.manager.hashes.set(`${method}:${routeId.bucketRoute}`, { value: hash, lastAccess: Date.now() });
} else if (hash) {
const hashData = this.manager.hashes.get(`${method}:${routeId.bucketRoute}`);
if (hashData) {
hashData.lastAccess = Date.now();
}
}
let sublimitTimeout = null;
if (retryAfter > 0) {
if (res.headers["x-ratelimit-global"] !== void 0) {
this.manager.globalRemaining = 0;
this.manager.globalReset = Date.now() + retryAfter;
} else if (!this.localLimited) {
sublimitTimeout = retryAfter;
}
}
if (status === 401 || status === 403 || status === 429) {
if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
invalidCount = 0;
}
invalidCount++;
const emitInvalid = this.manager.options.invalidRequestWarningInterval > 0 && invalidCount % this.manager.options.invalidRequestWarningInterval === 0;
if (emitInvalid) {
this.manager.emit("invalidRequestWarning" /* InvalidRequestWarning */, {
count: invalidCount,
remainingTime: invalidCountResetTime - Date.now()
});
}
}
if (status >= 200 && status < 300) {
return res;
} else if (status === 429) {
const isGlobal = this.globalLimited;
let limit2;
let timeout2;
if (isGlobal) {
limit2 = this.manager.options.globalRequestsPerSecond;
timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
} else {
limit2 = this.limit;
timeout2 = this.timeToReset;
}
await this.onRateLimit({
timeToReset: timeout2,
limit: limit2,
method,
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal
});
this.debug(
[
"Encountered unexpected 429 rate limit",
` Global : ${isGlobal.toString()}`,
` Method : ${method}`,
` URL : ${url}`,
` Bucket : ${routeId.bucketRoute}`,
` Major parameter: ${routeId.majorParameter}`,
` Hash : ${this.hash}`,
` Limit : ${limit2}`,
` Retry After : ${retryAfter}ms`,
` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : "None"}`
].join("\n")
);
if (sublimitTimeout) {
const firstSublimit = !this.#sublimitedQueue;
if (firstSublimit) {
this.#sublimitedQueue = new import_async_queue.AsyncQueue();
void this.#sublimitedQueue.wait();
this.#asyncQueue.shift();
}
this.#sublimitPromise?.resolve();
this.#sublimitPromise = null;
await (0, import_promises.setTimeout)(sublimitTimeout);
let resolve;
const promise = new Promise((res2) => resolve = res2);
this.#sublimitPromise = { promise, resolve };
if (firstSublimit) {
await this.#asyncQueue.wait();
this.#shiftSublimit = true;
}
}
return this.runRequest(routeId, url, options, requestData, retries);
} else if (status >= 500 && status < 600) {
if (retries !== this.manager.options.retries) {
return this.runRequest(routeId, url, options, requestData, ++retries);
}
throw new HTTPError(status, method, url, requestData);
} else {
if (status >= 400 && status < 500) {
if (status === 401 && requestData.auth) {
this.manager.setToken(null);
}
const data = await parseResponse(res);
throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
}
return res;
}
}
};
__name(SequentialHandler, "SequentialHandler");
// src/lib/RequestManager.ts
var getFileType = (0, import_util.lazy)(async () => import("file-type"));
var RequestMethod = /* @__PURE__ */ ((RequestMethod2) => {
RequestMethod2["Delete"] = "DELETE";
RequestMethod2["Get"] = "GET";
RequestMethod2["Patch"] = "PATCH";
RequestMethod2["Post"] = "POST";
RequestMethod2["Put"] = "PUT";
return RequestMethod2;
})(RequestMethod || {});
var RequestManager = class extends import_node_events.EventEmitter {
agent = null;
globalRemaining;
globalDelay = null;
globalReset = -1;
hashes = new import_collection.Collection();
handlers = new import_collection.Collection();
#token = null;
hashTimer;
handlerTimer;
options;
constructor(options) {
super();
this.options = { ...DefaultRestOptions, ...options };
this.options.offset = Math.max(0, this.options.offset);
this.globalRemaining = this.options.globalRequestsPerSecond;
this.agent = options.agent ?? null;
this.setupSweepers();
}
setupSweepers() {
const validateMaxInterval = /* @__PURE__ */ __name((interval) => {
if (interval > 144e5) {
throw new Error("Cannot set an interval greater than 4 hours");
}
}, "validateMaxInterval");
if (this.options.hashSweepInterval !== 0 && this.options.hashSweepInterval !== Number.POSITIVE_INFINITY) {
validateMaxInterval(this.options.hashSweepInterval);
this.hashTimer = (0, import_node_timers2.setInterval)(() => {
const sweptHashes = new import_collection.Collection();
const currentDate = Date.now();
this.hashes.sweep((val, key) => {
if (val.lastAccess === -1)
return false;
const shouldSweep = Math.floor(currentDate - val.lastAccess) > this.options.hashLifetime;
if (shouldSweep) {
sweptHashes.set(key, val);
}
this.emit("restDebug" /* Debug */, `Hash ${val.value} for ${key} swept due to lifetime being exceeded`);
return shouldSweep;
});
this.emit("hashSweep" /* HashSweep */, sweptHashes);
}, this.options.hashSweepInterval).unref();
}
if (this.options.handlerSweepInterval !== 0 && this.options.handlerSweepInterval !== Number.POSITIVE_INFINITY) {
validateMaxInterval(this.options.handlerSweepInterval);
this.handlerTimer = (0, import_node_timers2.setInterval)(() => {
const sweptHandlers = new import_collection.Collection();
this.handlers.sweep((val, key) => {
const { inactive } = val;
if (inactive) {
sweptHandlers.set(key, val);
}
this.emit("restDebug" /* Debug */, `Handler ${val.id} for ${key} swept due to being inactive`);
return inactive;
});
this.emit("handlerSweep" /* HandlerSweep */, sweptHandlers);
}, this.options.handlerSweepInterval).unref();
}
}
setAgent(agent) {
this.agent = agent;
return this;
}
setToken(token) {
this.#token = token;
return this;
}
async queueRequest(request2) {
const routeId = RequestManager.generateRouteData(request2.fullRoute, request2.method);
const hash = this.hashes.get(`${request2.method}:${routeId.bucketRoute}`) ?? {
value: `Global(${request2.method}:${routeId.bucketRoute})`,
lastAccess: -1
};
const handler = this.handlers.get(`${hash.value}:${routeId.majorParameter}`) ?? this.createHandler(hash.value, routeId.majorParameter);
const { url, fetchOptions } = await this.resolveRequest(request2);
return handler.queueRequest(routeId, url, fetchOptions, {
body: request2.body,
files: request2.files,
auth: request2.auth !== false,
signal: request2.signal
});
}
createHandler(hash, majorParameter) {
const queue = new SequentialHandler(this, hash, majorParameter);
this.handlers.set(queue.id, queue);
return queue;
}
async resolveRequest(request2) {
const { options } = this;
let query = "";
if (request2.query) {
const resolvedQuery = request2.query.toString();
if (resolvedQuery !== "") {
query = `?${resolvedQuery}`;
}
}
const headers = {
...this.options.headers,
"User-Agent": `${DefaultUserAgent} ${options.userAgentAppendix}`.trim()
};
if (request2.auth !== false) {
if (!this.#token) {
throw new Error("Expected token to be set for this request, but none was present");
}
headers.Authorization = `${request2.authPrefix ?? this.options.authPrefix} ${this.#token}`;
}
if (request2.reason?.length) {
headers["X-Audit-Log-Reason"] = encodeURIComponent(request2.reason);
}
const url = `${options.api}${request2.versioned === false ? "" : `/v${options.version}`}${request2.fullRoute}${query}`;
let finalBody;
let additionalHeaders = {};
if (request2.files?.length) {
const formData = new import_undici4.FormData();
for (const [index, file] of request2.files.entries()) {
const fileKey = file.key ?? `files[${index}]`;
if (import_node_buffer2.Buffer.isBuffer(file.data)) {
const { fileTypeFromBuffer } = await getFileType();
const contentType = file.contentType ?? (await fileTypeFromBuffer(file.data))?.mime;
formData.append(fileKey, new import_node_buffer2.Blob([file.data], { type: contentType }), file.name);
} else {
formData.append(fileKey, new import_node_buffer2.Blob([`${file.data}`], { type: file.contentType }), file.name);
}
}
if (request2.body != null) {
if (request2.appendToFormData) {
for (const [key, value] of Object.entries(request2.body)) {
formData.append(key, value);
}
} else {
formData.append("payload_json", JSON.stringify(request2.body));
}
}
finalBody = formData;
} else if (request2.body != null) {
if (request2.passThroughBody) {
finalBody = request2.body;
} else {
finalBody = JSON.stringify(request2.body);
additionalHeaders = { "Content-Type": "application/json" };
}
}
finalBody = await resolveBody(finalBody);
const fetchOptions = {
headers: { ...request2.headers, ...additionalHeaders, ...headers },
method: request2.method.toUpperCase()
};
if (finalBody !== void 0) {
fetchOptions.body = finalBody;
}
fetchOptions.dispatcher = request2.dispatcher ?? this.agent ?? void 0;
return { url, fetchOptions };
}
clearHashSweeper() {
(0, import_node_timers2.clearInterval)(this.hashTimer);
}
clearHandlerSweeper() {
(0, import_node_timers2.clearInterval)(this.handlerTimer);
}
static generateRouteData(endpoint, method) {
const majorIdMatch = /^\/(?:channels|guilds|webhooks)\/(\d{16,19})/.exec(endpoint);
const majorId = majorIdMatch?.[1] ?? "global";
const baseRoute = endpoint.replaceAll(/\d{16,19}/g, ":id").replace(/\/reactions\/(.*)/, "/reactions/:reaction");
let exceptions = "";
if (method === "DELETE" /* Delete */ && baseRoute === "/channels/:id/messages/:id") {
const id = /\d{16,19}$/.exec(endpoint)[0];
const timestamp = import_snowflake.DiscordSnowflake.timestampFrom(id);
if (Date.now() - timestamp > 1e3 * 60 * 60 * 24 * 14) {
exceptions += "/Delete Old Message";
}
}
return {
majorParameter: majorId,
bucketRoute: baseRoute + exceptions,
original: endpoint
};
}
};
__name(RequestManager, "RequestManager");
// src/lib/REST.ts
var import_node_events2 = require("events");
var REST = class extends import_node_events2.EventEmitter {
cdn;
requestManager;
constructor(options = {}) {
super();
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
this.requestManager = new RequestManager(options).on("restDebug" /* Debug */, this.emit.bind(this, "restDebug" /* Debug */)).on("rateLimited" /* RateLimited */, this.emit.bind(this, "rateLimited" /* RateLimited */)).on("invalidRequestWarning" /* InvalidRequestWarning */, this.emit.bind(this, "invalidRequestWarning" /* InvalidRequestWarning */)).on("hashSweep" /* HashSweep */, this.emit.bind(this, "hashSweep" /* HashSweep */));
this.on("newListener", (name, listener) => {
if (name === "response" /* Response */)
this.requestManager.on(name, listener);
});
this.on("removeListener", (name, listener) => {
if (name === "response" /* Response */)
this.requestManager.off(name, listener);
});
}
getAgent() {
return this.requestManager.agent;
}
setAgent(agent) {
this.requestManager.setAgent(agent);
return this;
}
setToken(token) {
this.requestManager.setToken(token);
return this;
}
async get(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "GET" /* Get */ });
}
async delete(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "DELETE" /* Delete */ });
}
async post(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "POST" /* Post */ });
}
async put(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "PUT" /* Put */ });
}
async patch(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "PATCH" /* Patch */ });
}
async request(options) {
const response = await this.raw(options);
return parseResponse(response);
}
async raw(options) {
return this.requestManager.queueRequest(options);
}
};
__name(REST, "REST");
// src/index.ts
var version = "1.5.0";
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ALLOWED_EXTENSIONS,
ALLOWED_SIZES,
ALLOWED_STICKER_EXTENSIONS,
CDN,
DefaultRestOptions,
DefaultUserAgent,
DiscordAPIError,
HTTPError,
REST,
RESTEvents,
RateLimitError,
RequestManager,
RequestMethod,
makeURLSearchParams,
parseResponse,
version
});
//# sourceMappingURL=index.js.map

1
node_modules/@discordjs/rest/dist/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

894
node_modules/@discordjs/rest/dist/index.mjs generated vendored Normal file
View file

@ -0,0 +1,894 @@
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/lib/CDN.ts
import { URL } from "node:url";
// src/lib/utils/constants.ts
import process from "node:process";
import { APIVersion } from "discord-api-types/v10";
import { Agent } from "undici";
var DefaultUserAgent = `DiscordBot (https://discord.js.org, 1.5.0)`;
var DefaultRestOptions = {
get agent() {
return new Agent({
connect: {
timeout: 3e4
}
});
},
api: "https://discord.com/api",
authPrefix: "Bot",
cdn: "https://cdn.discordapp.com",
headers: {},
invalidRequestWarningInterval: 0,
globalRequestsPerSecond: 50,
offset: 50,
rejectOnRateLimit: null,
retries: 3,
timeout: 15e3,
userAgentAppendix: `Node.js ${process.version}`,
version: APIVersion,
hashSweepInterval: 144e5,
hashLifetime: 864e5,
handlerSweepInterval: 36e5
};
var RESTEvents = /* @__PURE__ */ ((RESTEvents2) => {
RESTEvents2["Debug"] = "restDebug";
RESTEvents2["HandlerSweep"] = "handlerSweep";
RESTEvents2["HashSweep"] = "hashSweep";
RESTEvents2["InvalidRequestWarning"] = "invalidRequestWarning";
RESTEvents2["RateLimited"] = "rateLimited";
RESTEvents2["Response"] = "response";
return RESTEvents2;
})(RESTEvents || {});
var ALLOWED_EXTENSIONS = ["webp", "png", "jpg", "jpeg", "gif"];
var ALLOWED_STICKER_EXTENSIONS = ["png", "json"];
var ALLOWED_SIZES = [16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
// src/lib/CDN.ts
var CDN = class {
constructor(base = DefaultRestOptions.cdn) {
this.base = base;
}
appAsset(clientId, assetHash, options) {
return this.makeURL(`/app-assets/${clientId}/${assetHash}`, options);
}
appIcon(clientId, iconHash, options) {
return this.makeURL(`/app-icons/${clientId}/${iconHash}`, options);
}
avatar(id, avatarHash, options) {
return this.dynamicMakeURL(`/avatars/${id}/${avatarHash}`, avatarHash, options);
}
banner(id, bannerHash, options) {
return this.dynamicMakeURL(`/banners/${id}/${bannerHash}`, bannerHash, options);
}
channelIcon(channelId, iconHash, options) {
return this.makeURL(`/channel-icons/${channelId}/${iconHash}`, options);
}
defaultAvatar(discriminator) {
return this.makeURL(`/embed/avatars/${discriminator}`, { extension: "png" });
}
discoverySplash(guildId, splashHash, options) {
return this.makeURL(`/discovery-splashes/${guildId}/${splashHash}`, options);
}
emoji(emojiId, extension) {
return this.makeURL(`/emojis/${emojiId}`, { extension });
}
guildMemberAvatar(guildId, userId, avatarHash, options) {
return this.dynamicMakeURL(`/guilds/${guildId}/users/${userId}/avatars/${avatarHash}`, avatarHash, options);
}
guildMemberBanner(guildId, userId, bannerHash, options) {
return this.dynamicMakeURL(`/guilds/${guildId}/users/${userId}/banner`, bannerHash, options);
}
icon(id, iconHash, options) {
return this.dynamicMakeURL(`/icons/${id}/${iconHash}`, iconHash, options);
}
roleIcon(roleId, roleIconHash, options) {
return this.makeURL(`/role-icons/${roleId}/${roleIconHash}`, options);
}
splash(guildId, splashHash, options) {
return this.makeURL(`/splashes/${guildId}/${splashHash}`, options);
}
sticker(stickerId, extension) {
return this.makeURL(`/stickers/${stickerId}`, {
allowedExtensions: ALLOWED_STICKER_EXTENSIONS,
extension: extension ?? "png"
});
}
stickerPackBanner(bannerId, options) {
return this.makeURL(`/app-assets/710982414301790216/store/${bannerId}`, options);
}
teamIcon(teamId, iconHash, options) {
return this.makeURL(`/team-icons/${teamId}/${iconHash}`, options);
}
guildScheduledEventCover(scheduledEventId, coverHash, options) {
return this.makeURL(`/guild-events/${scheduledEventId}/${coverHash}`, options);
}
dynamicMakeURL(route, hash, { forceStatic = false, ...options } = {}) {
return this.makeURL(route, !forceStatic && hash.startsWith("a_") ? { ...options, extension: "gif" } : options);
}
makeURL(route, { allowedExtensions = ALLOWED_EXTENSIONS, extension = "webp", size } = {}) {
extension = String(extension).toLowerCase();
if (!allowedExtensions.includes(extension)) {
throw new RangeError(`Invalid extension provided: ${extension}
Must be one of: ${allowedExtensions.join(", ")}`);
}
if (size && !ALLOWED_SIZES.includes(size)) {
throw new RangeError(`Invalid size provided: ${size}
Must be one of: ${ALLOWED_SIZES.join(", ")}`);
}
const url = new URL(`${this.base}${route}.${extension}`);
if (size) {
url.searchParams.set("size", String(size));
}
return url.toString();
}
};
__name(CDN, "CDN");
// src/lib/errors/DiscordAPIError.ts
function isErrorGroupWrapper(error) {
return Reflect.has(error, "_errors");
}
__name(isErrorGroupWrapper, "isErrorGroupWrapper");
function isErrorResponse(error) {
return typeof Reflect.get(error, "message") === "string";
}
__name(isErrorResponse, "isErrorResponse");
var DiscordAPIError = class extends Error {
constructor(rawError, code, status, method, url, bodyData) {
super(DiscordAPIError.getMessage(rawError));
this.rawError = rawError;
this.code = code;
this.status = status;
this.method = method;
this.url = url;
this.requestBody = { files: bodyData.files, json: bodyData.body };
}
requestBody;
get name() {
return `${DiscordAPIError.name}[${this.code}]`;
}
static getMessage(error) {
let flattened = "";
if ("code" in error) {
if (error.errors) {
flattened = [...this.flattenDiscordError(error.errors)].join("\n");
}
return error.message && flattened ? `${error.message}
${flattened}` : error.message || flattened || "Unknown Error";
}
return error.error_description ?? "No Description";
}
static *flattenDiscordError(obj, key = "") {
if (isErrorResponse(obj)) {
return yield `${key.length ? `${key}[${obj.code}]` : `${obj.code}`}: ${obj.message}`.trim();
}
for (const [otherKey, val] of Object.entries(obj)) {
const nextKey = otherKey.startsWith("_") ? key : key ? Number.isNaN(Number(otherKey)) ? `${key}.${otherKey}` : `${key}[${otherKey}]` : otherKey;
if (typeof val === "string") {
yield val;
} else if (isErrorGroupWrapper(val)) {
for (const error of val._errors) {
yield* this.flattenDiscordError(error, nextKey);
}
} else {
yield* this.flattenDiscordError(val, nextKey);
}
}
}
};
__name(DiscordAPIError, "DiscordAPIError");
// src/lib/errors/HTTPError.ts
import { STATUS_CODES } from "node:http";
var HTTPError = class extends Error {
constructor(status, method, url, bodyData) {
super(STATUS_CODES[status]);
this.status = status;
this.method = method;
this.url = url;
this.requestBody = { files: bodyData.files, json: bodyData.body };
}
requestBody;
name = HTTPError.name;
};
__name(HTTPError, "HTTPError");
// src/lib/errors/RateLimitError.ts
var RateLimitError = class extends Error {
timeToReset;
limit;
method;
hash;
url;
route;
majorParameter;
global;
constructor({ timeToReset, limit, method, hash, url, route, majorParameter, global }) {
super();
this.timeToReset = timeToReset;
this.limit = limit;
this.method = method;
this.hash = hash;
this.url = url;
this.route = route;
this.majorParameter = majorParameter;
this.global = global;
}
get name() {
return `${RateLimitError.name}[${this.route}]`;
}
};
__name(RateLimitError, "RateLimitError");
// src/lib/RequestManager.ts
import { Blob as Blob2, Buffer as Buffer3 } from "node:buffer";
import { EventEmitter } from "node:events";
import { setInterval, clearInterval } from "node:timers";
import { Collection } from "@discordjs/collection";
import { lazy } from "@discordjs/util";
import { DiscordSnowflake } from "@sapphire/snowflake";
import { FormData as FormData2 } from "undici";
// src/lib/handlers/SequentialHandler.ts
import { setTimeout, clearTimeout } from "node:timers";
import { setTimeout as sleep } from "node:timers/promises";
import { AsyncQueue } from "@sapphire/async-queue";
import { request } from "undici";
// src/lib/utils/utils.ts
import { Blob, Buffer as Buffer2 } from "node:buffer";
import { URLSearchParams } from "node:url";
import { types } from "node:util";
import { FormData } from "undici";
function parseHeader(header) {
if (header === void 0 || typeof header === "string") {
return header;
}
return header.join(";");
}
__name(parseHeader, "parseHeader");
function serializeSearchParam(value) {
switch (typeof value) {
case "string":
return value;
case "number":
case "bigint":
case "boolean":
return value.toString();
case "object":
if (value === null)
return null;
if (value instanceof Date) {
return Number.isNaN(value.getTime()) ? null : value.toISOString();
}
if (typeof value.toString === "function" && value.toString !== Object.prototype.toString)
return value.toString();
return null;
default:
return null;
}
}
__name(serializeSearchParam, "serializeSearchParam");
function makeURLSearchParams(options) {
const params = new URLSearchParams();
if (!options)
return params;
for (const [key, value] of Object.entries(options)) {
const serialized = serializeSearchParam(value);
if (serialized !== null)
params.append(key, serialized);
}
return params;
}
__name(makeURLSearchParams, "makeURLSearchParams");
async function parseResponse(res) {
const header = parseHeader(res.headers["content-type"]);
if (header?.startsWith("application/json")) {
return res.body.json();
}
return res.body.arrayBuffer();
}
__name(parseResponse, "parseResponse");
function hasSublimit(bucketRoute, body, method) {
if (bucketRoute === "/channels/:id") {
if (typeof body !== "object" || body === null)
return false;
if (method !== "PATCH" /* Patch */)
return false;
const castedBody = body;
return ["name", "topic"].some((key) => Reflect.has(castedBody, key));
}
return true;
}
__name(hasSublimit, "hasSublimit");
async function resolveBody(body) {
if (body == null) {
return null;
} else if (typeof body === "string") {
return body;
} else if (types.isUint8Array(body)) {
return body;
} else if (types.isArrayBuffer(body)) {
return new Uint8Array(body);
} else if (body instanceof URLSearchParams) {
return body.toString();
} else if (body instanceof DataView) {
return new Uint8Array(body.buffer);
} else if (body instanceof Blob) {
return new Uint8Array(await body.arrayBuffer());
} else if (body instanceof FormData) {
return body;
} else if (body[Symbol.iterator]) {
const chunks = [...body];
const length = chunks.reduce((a, b) => a + b.length, 0);
const uint8 = new Uint8Array(length);
let lengthUsed = 0;
return chunks.reduce((a, b) => {
a.set(b, lengthUsed);
lengthUsed += b.length;
return a;
}, uint8);
} else if (body[Symbol.asyncIterator]) {
const chunks = [];
for await (const chunk of body) {
chunks.push(chunk);
}
return Buffer2.concat(chunks);
}
throw new TypeError(`Unable to resolve body.`);
}
__name(resolveBody, "resolveBody");
function shouldRetry(error) {
if (error.name === "AbortError")
return true;
return "code" in error && error.code === "ECONNRESET" || error.message.includes("ECONNRESET");
}
__name(shouldRetry, "shouldRetry");
// src/lib/handlers/SequentialHandler.ts
var invalidCount = 0;
var invalidCountResetTime = null;
var SequentialHandler = class {
constructor(manager, hash, majorParameter) {
this.manager = manager;
this.hash = hash;
this.majorParameter = majorParameter;
this.id = `${hash}:${majorParameter}`;
}
id;
reset = -1;
remaining = 1;
limit = Number.POSITIVE_INFINITY;
#asyncQueue = new AsyncQueue();
#sublimitedQueue = null;
#sublimitPromise = null;
#shiftSublimit = false;
get inactive() {
return this.#asyncQueue.remaining === 0 && (this.#sublimitedQueue === null || this.#sublimitedQueue.remaining === 0) && !this.limited;
}
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 timeToReset() {
return this.reset + this.manager.options.offset - Date.now();
}
debug(message) {
this.manager.emit("restDebug" /* Debug */, `[REST ${this.id}] ${message}`);
}
async globalDelayFor(time) {
await sleep(time);
this.manager.globalDelay = null;
}
async onRateLimit(rateLimitData) {
const { options } = this.manager;
if (!options.rejectOnRateLimit)
return;
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 queueRequest(routeId, url, options, requestData) {
let queue = this.#asyncQueue;
let queueType = 0 /* Standard */;
if (this.#sublimitedQueue && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
queue = this.#sublimitedQueue;
queueType = 1 /* Sublimit */;
}
await queue.wait({ signal: requestData.signal });
if (queueType === 0 /* Standard */) {
if (this.#sublimitedQueue && hasSublimit(routeId.bucketRoute, requestData.body, options.method)) {
queue = this.#sublimitedQueue;
const wait = queue.wait();
this.#asyncQueue.shift();
await wait;
} else if (this.#sublimitPromise) {
await this.#sublimitPromise.promise;
}
}
try {
return await this.runRequest(routeId, url, options, requestData);
} finally {
queue.shift();
if (this.#shiftSublimit) {
this.#shiftSublimit = false;
this.#sublimitedQueue?.shift();
}
if (this.#sublimitedQueue?.remaining === 0) {
this.#sublimitPromise?.resolve();
this.#sublimitedQueue = null;
}
}
}
async runRequest(routeId, url, options, requestData, retries = 0) {
while (this.limited) {
const isGlobal = this.globalLimited;
let limit2;
let timeout2;
let delay;
if (isGlobal) {
limit2 = this.manager.options.globalRequestsPerSecond;
timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
if (!this.manager.globalDelay) {
this.manager.globalDelay = this.globalDelayFor(timeout2);
}
delay = this.manager.globalDelay;
} else {
limit2 = this.limit;
timeout2 = this.timeToReset;
delay = sleep(timeout2);
}
const rateLimitData = {
timeToReset: timeout2,
limit: limit2,
method: options.method ?? "get",
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal
};
this.manager.emit("rateLimited" /* RateLimited */, rateLimitData);
await this.onRateLimit(rateLimitData);
if (isGlobal) {
this.debug(`Global rate limit hit, blocking all requests for ${timeout2}ms`);
} else {
this.debug(`Waiting ${timeout2}ms for rate limit to pass`);
}
await delay;
}
if (!this.manager.globalReset || this.manager.globalReset < Date.now()) {
this.manager.globalReset = Date.now() + 1e3;
this.manager.globalRemaining = this.manager.options.globalRequestsPerSecond;
}
this.manager.globalRemaining--;
const method = options.method ?? "get";
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.manager.options.timeout).unref();
if (requestData.signal) {
const signal = requestData.signal;
if (signal.aborted)
controller.abort();
else
signal.addEventListener("abort", () => controller.abort());
}
let res;
try {
res = await request(url, { ...options, signal: controller.signal });
} catch (error) {
if (!(error instanceof Error))
throw error;
if (shouldRetry(error) && retries !== this.manager.options.retries) {
return await this.runRequest(routeId, url, options, requestData, ++retries);
}
throw error;
} finally {
clearTimeout(timeout);
}
if (this.manager.listenerCount("response" /* Response */)) {
this.manager.emit(
"response" /* Response */,
{
method,
path: routeId.original,
route: routeId.bucketRoute,
options,
data: requestData,
retries
},
{ ...res }
);
}
const status = res.statusCode;
let retryAfter = 0;
const limit = parseHeader(res.headers["x-ratelimit-limit"]);
const remaining = parseHeader(res.headers["x-ratelimit-remaining"]);
const reset = parseHeader(res.headers["x-ratelimit-reset-after"]);
const hash = parseHeader(res.headers["x-ratelimit-bucket"]);
const retry = parseHeader(res.headers["retry-after"]);
this.limit = limit ? Number(limit) : Number.POSITIVE_INFINITY;
this.remaining = remaining ? Number(remaining) : 1;
this.reset = reset ? Number(reset) * 1e3 + Date.now() + this.manager.options.offset : Date.now();
if (retry)
retryAfter = Number(retry) * 1e3 + this.manager.options.offset;
if (hash && hash !== this.hash) {
this.debug(["Received bucket hash update", ` Old Hash : ${this.hash}`, ` New Hash : ${hash}`].join("\n"));
this.manager.hashes.set(`${method}:${routeId.bucketRoute}`, { value: hash, lastAccess: Date.now() });
} else if (hash) {
const hashData = this.manager.hashes.get(`${method}:${routeId.bucketRoute}`);
if (hashData) {
hashData.lastAccess = Date.now();
}
}
let sublimitTimeout = null;
if (retryAfter > 0) {
if (res.headers["x-ratelimit-global"] !== void 0) {
this.manager.globalRemaining = 0;
this.manager.globalReset = Date.now() + retryAfter;
} else if (!this.localLimited) {
sublimitTimeout = retryAfter;
}
}
if (status === 401 || status === 403 || status === 429) {
if (!invalidCountResetTime || invalidCountResetTime < Date.now()) {
invalidCountResetTime = Date.now() + 1e3 * 60 * 10;
invalidCount = 0;
}
invalidCount++;
const emitInvalid = this.manager.options.invalidRequestWarningInterval > 0 && invalidCount % this.manager.options.invalidRequestWarningInterval === 0;
if (emitInvalid) {
this.manager.emit("invalidRequestWarning" /* InvalidRequestWarning */, {
count: invalidCount,
remainingTime: invalidCountResetTime - Date.now()
});
}
}
if (status >= 200 && status < 300) {
return res;
} else if (status === 429) {
const isGlobal = this.globalLimited;
let limit2;
let timeout2;
if (isGlobal) {
limit2 = this.manager.options.globalRequestsPerSecond;
timeout2 = this.manager.globalReset + this.manager.options.offset - Date.now();
} else {
limit2 = this.limit;
timeout2 = this.timeToReset;
}
await this.onRateLimit({
timeToReset: timeout2,
limit: limit2,
method,
hash: this.hash,
url,
route: routeId.bucketRoute,
majorParameter: this.majorParameter,
global: isGlobal
});
this.debug(
[
"Encountered unexpected 429 rate limit",
` Global : ${isGlobal.toString()}`,
` Method : ${method}`,
` URL : ${url}`,
` Bucket : ${routeId.bucketRoute}`,
` Major parameter: ${routeId.majorParameter}`,
` Hash : ${this.hash}`,
` Limit : ${limit2}`,
` Retry After : ${retryAfter}ms`,
` Sublimit : ${sublimitTimeout ? `${sublimitTimeout}ms` : "None"}`
].join("\n")
);
if (sublimitTimeout) {
const firstSublimit = !this.#sublimitedQueue;
if (firstSublimit) {
this.#sublimitedQueue = new AsyncQueue();
void this.#sublimitedQueue.wait();
this.#asyncQueue.shift();
}
this.#sublimitPromise?.resolve();
this.#sublimitPromise = null;
await sleep(sublimitTimeout);
let resolve;
const promise = new Promise((res2) => resolve = res2);
this.#sublimitPromise = { promise, resolve };
if (firstSublimit) {
await this.#asyncQueue.wait();
this.#shiftSublimit = true;
}
}
return this.runRequest(routeId, url, options, requestData, retries);
} else if (status >= 500 && status < 600) {
if (retries !== this.manager.options.retries) {
return this.runRequest(routeId, url, options, requestData, ++retries);
}
throw new HTTPError(status, method, url, requestData);
} else {
if (status >= 400 && status < 500) {
if (status === 401 && requestData.auth) {
this.manager.setToken(null);
}
const data = await parseResponse(res);
throw new DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
}
return res;
}
}
};
__name(SequentialHandler, "SequentialHandler");
// src/lib/RequestManager.ts
var getFileType = lazy(async () => import("file-type"));
var RequestMethod = /* @__PURE__ */ ((RequestMethod2) => {
RequestMethod2["Delete"] = "DELETE";
RequestMethod2["Get"] = "GET";
RequestMethod2["Patch"] = "PATCH";
RequestMethod2["Post"] = "POST";
RequestMethod2["Put"] = "PUT";
return RequestMethod2;
})(RequestMethod || {});
var RequestManager = class extends EventEmitter {
agent = null;
globalRemaining;
globalDelay = null;
globalReset = -1;
hashes = new Collection();
handlers = new Collection();
#token = null;
hashTimer;
handlerTimer;
options;
constructor(options) {
super();
this.options = { ...DefaultRestOptions, ...options };
this.options.offset = Math.max(0, this.options.offset);
this.globalRemaining = this.options.globalRequestsPerSecond;
this.agent = options.agent ?? null;
this.setupSweepers();
}
setupSweepers() {
const validateMaxInterval = /* @__PURE__ */ __name((interval) => {
if (interval > 144e5) {
throw new Error("Cannot set an interval greater than 4 hours");
}
}, "validateMaxInterval");
if (this.options.hashSweepInterval !== 0 && this.options.hashSweepInterval !== Number.POSITIVE_INFINITY) {
validateMaxInterval(this.options.hashSweepInterval);
this.hashTimer = setInterval(() => {
const sweptHashes = new Collection();
const currentDate = Date.now();
this.hashes.sweep((val, key) => {
if (val.lastAccess === -1)
return false;
const shouldSweep = Math.floor(currentDate - val.lastAccess) > this.options.hashLifetime;
if (shouldSweep) {
sweptHashes.set(key, val);
}
this.emit("restDebug" /* Debug */, `Hash ${val.value} for ${key} swept due to lifetime being exceeded`);
return shouldSweep;
});
this.emit("hashSweep" /* HashSweep */, sweptHashes);
}, this.options.hashSweepInterval).unref();
}
if (this.options.handlerSweepInterval !== 0 && this.options.handlerSweepInterval !== Number.POSITIVE_INFINITY) {
validateMaxInterval(this.options.handlerSweepInterval);
this.handlerTimer = setInterval(() => {
const sweptHandlers = new Collection();
this.handlers.sweep((val, key) => {
const { inactive } = val;
if (inactive) {
sweptHandlers.set(key, val);
}
this.emit("restDebug" /* Debug */, `Handler ${val.id} for ${key} swept due to being inactive`);
return inactive;
});
this.emit("handlerSweep" /* HandlerSweep */, sweptHandlers);
}, this.options.handlerSweepInterval).unref();
}
}
setAgent(agent) {
this.agent = agent;
return this;
}
setToken(token) {
this.#token = token;
return this;
}
async queueRequest(request2) {
const routeId = RequestManager.generateRouteData(request2.fullRoute, request2.method);
const hash = this.hashes.get(`${request2.method}:${routeId.bucketRoute}`) ?? {
value: `Global(${request2.method}:${routeId.bucketRoute})`,
lastAccess: -1
};
const handler = this.handlers.get(`${hash.value}:${routeId.majorParameter}`) ?? this.createHandler(hash.value, routeId.majorParameter);
const { url, fetchOptions } = await this.resolveRequest(request2);
return handler.queueRequest(routeId, url, fetchOptions, {
body: request2.body,
files: request2.files,
auth: request2.auth !== false,
signal: request2.signal
});
}
createHandler(hash, majorParameter) {
const queue = new SequentialHandler(this, hash, majorParameter);
this.handlers.set(queue.id, queue);
return queue;
}
async resolveRequest(request2) {
const { options } = this;
let query = "";
if (request2.query) {
const resolvedQuery = request2.query.toString();
if (resolvedQuery !== "") {
query = `?${resolvedQuery}`;
}
}
const headers = {
...this.options.headers,
"User-Agent": `${DefaultUserAgent} ${options.userAgentAppendix}`.trim()
};
if (request2.auth !== false) {
if (!this.#token) {
throw new Error("Expected token to be set for this request, but none was present");
}
headers.Authorization = `${request2.authPrefix ?? this.options.authPrefix} ${this.#token}`;
}
if (request2.reason?.length) {
headers["X-Audit-Log-Reason"] = encodeURIComponent(request2.reason);
}
const url = `${options.api}${request2.versioned === false ? "" : `/v${options.version}`}${request2.fullRoute}${query}`;
let finalBody;
let additionalHeaders = {};
if (request2.files?.length) {
const formData = new FormData2();
for (const [index, file] of request2.files.entries()) {
const fileKey = file.key ?? `files[${index}]`;
if (Buffer3.isBuffer(file.data)) {
const { fileTypeFromBuffer } = await getFileType();
const contentType = file.contentType ?? (await fileTypeFromBuffer(file.data))?.mime;
formData.append(fileKey, new Blob2([file.data], { type: contentType }), file.name);
} else {
formData.append(fileKey, new Blob2([`${file.data}`], { type: file.contentType }), file.name);
}
}
if (request2.body != null) {
if (request2.appendToFormData) {
for (const [key, value] of Object.entries(request2.body)) {
formData.append(key, value);
}
} else {
formData.append("payload_json", JSON.stringify(request2.body));
}
}
finalBody = formData;
} else if (request2.body != null) {
if (request2.passThroughBody) {
finalBody = request2.body;
} else {
finalBody = JSON.stringify(request2.body);
additionalHeaders = { "Content-Type": "application/json" };
}
}
finalBody = await resolveBody(finalBody);
const fetchOptions = {
headers: { ...request2.headers, ...additionalHeaders, ...headers },
method: request2.method.toUpperCase()
};
if (finalBody !== void 0) {
fetchOptions.body = finalBody;
}
fetchOptions.dispatcher = request2.dispatcher ?? this.agent ?? void 0;
return { url, fetchOptions };
}
clearHashSweeper() {
clearInterval(this.hashTimer);
}
clearHandlerSweeper() {
clearInterval(this.handlerTimer);
}
static generateRouteData(endpoint, method) {
const majorIdMatch = /^\/(?:channels|guilds|webhooks)\/(\d{16,19})/.exec(endpoint);
const majorId = majorIdMatch?.[1] ?? "global";
const baseRoute = endpoint.replaceAll(/\d{16,19}/g, ":id").replace(/\/reactions\/(.*)/, "/reactions/:reaction");
let exceptions = "";
if (method === "DELETE" /* Delete */ && baseRoute === "/channels/:id/messages/:id") {
const id = /\d{16,19}$/.exec(endpoint)[0];
const timestamp = DiscordSnowflake.timestampFrom(id);
if (Date.now() - timestamp > 1e3 * 60 * 60 * 24 * 14) {
exceptions += "/Delete Old Message";
}
}
return {
majorParameter: majorId,
bucketRoute: baseRoute + exceptions,
original: endpoint
};
}
};
__name(RequestManager, "RequestManager");
// src/lib/REST.ts
import { EventEmitter as EventEmitter2 } from "node:events";
var REST = class extends EventEmitter2 {
cdn;
requestManager;
constructor(options = {}) {
super();
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
this.requestManager = new RequestManager(options).on("restDebug" /* Debug */, this.emit.bind(this, "restDebug" /* Debug */)).on("rateLimited" /* RateLimited */, this.emit.bind(this, "rateLimited" /* RateLimited */)).on("invalidRequestWarning" /* InvalidRequestWarning */, this.emit.bind(this, "invalidRequestWarning" /* InvalidRequestWarning */)).on("hashSweep" /* HashSweep */, this.emit.bind(this, "hashSweep" /* HashSweep */));
this.on("newListener", (name, listener) => {
if (name === "response" /* Response */)
this.requestManager.on(name, listener);
});
this.on("removeListener", (name, listener) => {
if (name === "response" /* Response */)
this.requestManager.off(name, listener);
});
}
getAgent() {
return this.requestManager.agent;
}
setAgent(agent) {
this.requestManager.setAgent(agent);
return this;
}
setToken(token) {
this.requestManager.setToken(token);
return this;
}
async get(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "GET" /* Get */ });
}
async delete(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "DELETE" /* Delete */ });
}
async post(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "POST" /* Post */ });
}
async put(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "PUT" /* Put */ });
}
async patch(fullRoute, options = {}) {
return this.request({ ...options, fullRoute, method: "PATCH" /* Patch */ });
}
async request(options) {
const response = await this.raw(options);
return parseResponse(response);
}
async raw(options) {
return this.requestManager.queueRequest(options);
}
};
__name(REST, "REST");
// src/index.ts
var version = "1.5.0";
export {
ALLOWED_EXTENSIONS,
ALLOWED_SIZES,
ALLOWED_STICKER_EXTENSIONS,
CDN,
DefaultRestOptions,
DefaultUserAgent,
DiscordAPIError,
HTTPError,
REST,
RESTEvents,
RateLimitError,
RequestManager,
RequestMethod,
makeURLSearchParams,
parseResponse,
version
};
//# sourceMappingURL=index.mjs.map

1
node_modules/@discordjs/rest/dist/index.mjs.map generated vendored Normal file

File diff suppressed because one or more lines are too long