forked from discordjs/discord.js-modules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
REST.ts
226 lines (207 loc) · 6.74 KB
/
REST.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import { EventEmitter } from 'node:events';
import { CDN } from './CDN';
import { InternalRequest, RequestData, RequestManager, RequestMethod, RouteLike } from './RequestManager';
import { DefaultRestOptions, RESTEvents } from './utils/constants';
/**
* Options to be passed when creating the REST instance
*/
export interface RESTOptions {
/**
* The base api path, without version
* @default 'https://discord.com/api'
*/
api: string;
/**
* The cdn path
* @default 'https://cdn.discordapp.com'
*/
cdn: string;
/**
* Additional headers to send for all API requests
* @default {}
*/
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.
* @default 0
*/
invalidRequestWarningInterval: number;
/**
* How many requests to allow sending per second (Infinity for unlimited, 50 for the standard global limit used by Discord)
* @default 50
*/
globalRequestsPerSecond: number;
/**
* The extra offset to add to rate limits in milliseconds
* @default 50
*/
offset: number;
/**
* Determines how 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 requests routes will be queued normally
* @default null
*/
rejectOnRateLimit: string[] | RateLimitQueueFilter | null;
/**
* The number of retries for errors with the 500 code, or errors
* that timeout
* @default 3
*/
retries: number;
/**
* The time to wait in milliseconds before a request is aborted
* @default 15_000
*/
timeout: number;
/**
* Extra information to add to the user agent
* @default `Node.js ${process.version}`
*/
userAgentAppendix: string;
/**
* The version of the API to use
* @default '9'
*/
version: string;
}
/**
* Data emitted on `RESTEvents.RateLimited`
*/
export interface RateLimitData {
/**
* The time, in milliseconds, until the request-lock is reset
*/
timeToReset: number;
/**
* The amount of requests we can perform before locking requests
*/
limit: number;
/**
* The HTTP method being performed
*/
method: string;
/**
* The bucket hash for this request
*/
hash: string;
/**
* The full url for this request
*/
url: string;
/**
* The route being hit in this request
*/
route: string;
/**
* 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;
/**
* Whether the rate limit that was reached was the global limit
*/
global: boolean;
}
/**
* A function that determines whether the rate limit hit should throw an Error
*/
export type RateLimitQueueFilter = (rateLimitData: RateLimitData) => boolean | Promise<boolean>;
export interface InvalidRequestWarningData {
/**
* Number of invalid requests that have been made in the window
*/
count: number;
/**
* Time in ms remaining before the count resets
*/
remainingTime: number;
}
export interface RestEvents {
invalidRequestWarning: [invalidRequestInfo: InvalidRequestWarningData];
restDebug: [info: string];
rateLimited: [rateLimitInfo: RateLimitData];
}
export interface REST {
on<K extends keyof RestEvents>(event: K, listener: (...args: RestEvents[K]) => void): this;
on<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;
once<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void): this;
emit<K extends keyof RestEvents>(event: K, ...args: RestEvents[K]): boolean;
emit<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;
off<S extends string | symbol>(event: Exclude<S, keyof RestEvents>, listener: (...args: any[]) => void): this;
removeAllListeners<K extends keyof RestEvents>(event?: K): this;
removeAllListeners<S extends string | symbol>(event?: Exclude<S, keyof RestEvents>): this;
}
export class REST extends EventEmitter {
public readonly cdn: CDN;
public readonly requestManager: RequestManager;
public constructor(options: Partial<RESTOptions> = {}) {
super();
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
this.requestManager = new RequestManager(options)
.on(RESTEvents.Debug, this.emit.bind(this, RESTEvents.Debug))
.on(RESTEvents.RateLimited, this.emit.bind(this, RESTEvents.RateLimited))
.on(RESTEvents.InvalidRequestWarning, this.emit.bind(this, RESTEvents.InvalidRequestWarning));
}
/**
* Sets the authorization token that should be used for requests
* @param token The authorization token to use
*/
public setToken(token: string) {
this.requestManager.setToken(token);
return this;
}
/**
* Runs a get request from the api
* @param fullRoute The full route to query
* @param options Optional request options
*/
public get(fullRoute: RouteLike, options: RequestData = {}) {
return this.request({ ...options, fullRoute, method: RequestMethod.Get });
}
/**
* Runs a delete request from the api
* @param fullRoute The full route to query
* @param options Optional request options
*/
public delete(fullRoute: RouteLike, options: RequestData = {}) {
return this.request({ ...options, fullRoute, method: RequestMethod.Delete });
}
/**
* Runs a post request from the api
* @param fullRoute The full route to query
* @param options Optional request options
*/
public post(fullRoute: RouteLike, options: RequestData = {}) {
return this.request({ ...options, fullRoute, method: RequestMethod.Post });
}
/**
* Runs a put request from the api
* @param fullRoute The full route to query
* @param options Optional request options
*/
public put(fullRoute: RouteLike, options: RequestData = {}) {
return this.request({ ...options, fullRoute, method: RequestMethod.Put });
}
/**
* Runs a patch request from the api
* @param fullRoute The full route to query
* @param options Optional request options
*/
public patch(fullRoute: RouteLike, options: RequestData = {}) {
return this.request({ ...options, fullRoute, method: RequestMethod.Patch });
}
/**
* Runs a request from the api
* @param options Request options
*/
public request(options: InternalRequest) {
return this.requestManager.queueRequest(options);
}
}