@discordeno/rest 19.0.0-next.0b02607 → 19.0.0-next.0d08c57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/invalidBucket.d.ts +6 -7
- package/dist/invalidBucket.d.ts.map +1 -1
- package/dist/invalidBucket.js +23 -25
- package/dist/invalidBucket.js.map +1 -1
- package/dist/manager.d.ts +9 -0
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +83 -42
- package/dist/manager.js.map +1 -1
- package/dist/routes.js +1 -1
- package/dist/routes.js.map +1 -1
- package/dist/types.d.ts +7 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -5
package/dist/invalidBucket.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
/**
|
|
3
2
|
* A invalid request bucket is used in a similar manner as a leaky bucket but a invalid request bucket can be refilled as needed.
|
|
4
3
|
* It's purpose is to make sure the bot does not hit the limit to getting a 1 hr ban.
|
|
@@ -14,8 +13,8 @@ export interface InvalidRequestBucketOptions {
|
|
|
14
13
|
max?: number;
|
|
15
14
|
/** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */
|
|
16
15
|
interval?: number;
|
|
17
|
-
/**
|
|
18
|
-
|
|
16
|
+
/** When the timeout for the bucket has started at. */
|
|
17
|
+
resetAt?: number;
|
|
19
18
|
/** how safe to be from max. Defaults to 1 */
|
|
20
19
|
safety?: number;
|
|
21
20
|
/** The request statuses that count as an invalid request. */
|
|
@@ -25,19 +24,19 @@ export interface InvalidRequestBucketOptions {
|
|
|
25
24
|
}
|
|
26
25
|
export interface InvalidRequestBucket {
|
|
27
26
|
/** current invalid amount */
|
|
28
|
-
|
|
27
|
+
invalidRequests: number;
|
|
29
28
|
/** max invalid requests allowed until ban. Defaults to 10,000 */
|
|
30
29
|
max: number;
|
|
31
30
|
/** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */
|
|
32
31
|
interval: number;
|
|
33
|
-
/**
|
|
34
|
-
|
|
32
|
+
/** When the timeout for this bucket has started at. */
|
|
33
|
+
resetAt: number | undefined;
|
|
35
34
|
/** how safe to be from max. Defaults to 1 */
|
|
36
35
|
safety: number;
|
|
37
36
|
/** The request statuses that count as an invalid request. */
|
|
38
37
|
errorStatuses: number[];
|
|
39
38
|
/** The amount of requests that were requested from this bucket. */
|
|
40
|
-
|
|
39
|
+
activeRequests: number;
|
|
41
40
|
/** The requests that are currently pending. */
|
|
42
41
|
waiting: Array<(value: void | PromiseLike<void>) => void>;
|
|
43
42
|
/** Whether or not the waiting queue is already processing. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invalidBucket.d.ts","sourceRoot":"","sources":["../src/invalidBucket.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"invalidBucket.d.ts","sourceRoot":"","sources":["../src/invalidBucket.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,oBAAoB,CAmFrG;AAED,MAAM,WAAW,2BAA2B;IAC1C,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sGAAsG;IACtG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,6BAA6B;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,sGAAsG;IACtG,QAAQ,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAA;IACtB,+CAA+C;IAC/C,OAAO,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAA;IACzD,8DAA8D;IAC9D,UAAU,EAAE,OAAO,CAAA;IAEnB,+DAA+D;IAC/D,eAAe,EAAE,MAAM,MAAM,CAAA;IAC7B,mDAAmD;IACnD,gBAAgB,EAAE,MAAM,OAAO,CAAA;IAC/B,yCAAyC;IACzC,yBAAyB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,uDAAuD;IACvD,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,iIAAiI;IACjI,sBAAsB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,KAAK,IAAI,CAAA;CACrE"}
|
package/dist/invalidBucket.js
CHANGED
|
@@ -7,21 +7,25 @@ import { delay, logger } from '@discordeno/utils';
|
|
|
7
7
|
* @returns RefillingBucket
|
|
8
8
|
*/ export function createInvalidRequestBucket(options) {
|
|
9
9
|
const bucket = {
|
|
10
|
-
|
|
10
|
+
invalidRequests: options.current ?? 0,
|
|
11
11
|
max: options.max ?? 10000,
|
|
12
|
-
interval: options.interval ??
|
|
13
|
-
|
|
12
|
+
interval: options.interval ?? 600_000,
|
|
13
|
+
resetAt: options.resetAt,
|
|
14
14
|
safety: options.safety ?? 1,
|
|
15
15
|
errorStatuses: options.errorStatuses ?? [
|
|
16
16
|
401,
|
|
17
17
|
403,
|
|
18
18
|
429
|
|
19
19
|
],
|
|
20
|
-
|
|
20
|
+
activeRequests: options.requested ?? 0,
|
|
21
21
|
processing: false,
|
|
22
22
|
waiting: [],
|
|
23
23
|
requestsAllowed: function() {
|
|
24
|
-
|
|
24
|
+
if (bucket.resetAt !== undefined && Date.now() >= bucket.resetAt) {
|
|
25
|
+
bucket.invalidRequests = 0;
|
|
26
|
+
bucket.resetAt = Date.now() + bucket.interval;
|
|
27
|
+
}
|
|
28
|
+
return bucket.max - bucket.invalidRequests - bucket.activeRequests - bucket.safety;
|
|
25
29
|
},
|
|
26
30
|
isRequestAllowed: function() {
|
|
27
31
|
return bucket.requestsAllowed() > 0;
|
|
@@ -31,7 +35,7 @@ import { delay, logger } from '@discordeno/utils';
|
|
|
31
35
|
return await new Promise(async (resolve)=>{
|
|
32
36
|
// If whatever amount of requests is left is more than the safety margin, allow the request
|
|
33
37
|
if (bucket.isRequestAllowed()) {
|
|
34
|
-
bucket.
|
|
38
|
+
bucket.activeRequests += 1;
|
|
35
39
|
resolve();
|
|
36
40
|
} else {
|
|
37
41
|
bucket.waiting.push(resolve);
|
|
@@ -41,41 +45,35 @@ import { delay, logger } from '@discordeno/utils';
|
|
|
41
45
|
},
|
|
42
46
|
processWaiting: async function() {
|
|
43
47
|
// If already processing, that loop will handle all waiting requests.
|
|
44
|
-
if (bucket.processing)
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
48
|
+
if (bucket.processing) return;
|
|
47
49
|
// Mark as processing so other loops don't start
|
|
48
50
|
bucket.processing = true;
|
|
49
51
|
while(bucket.waiting.length > 0){
|
|
50
|
-
logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length}
|
|
51
|
-
if (bucket.isRequestAllowed()) {
|
|
52
|
-
bucket.
|
|
53
|
-
|
|
54
|
-
bucket.waiting.shift()?.();
|
|
55
|
-
} else {
|
|
56
|
-
await delay(1000);
|
|
52
|
+
logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} pending requests to be made. ${JSON.stringify(bucket)}`);
|
|
53
|
+
if (!bucket.isRequestAllowed() && bucket.resetAt !== undefined) {
|
|
54
|
+
logger.warn(`[InvalidBucket] processing waiting queue is now paused until more requests are available. ${bucket.waiting.length} pending requests. ${JSON.stringify(bucket)}`);
|
|
55
|
+
await delay(bucket.resetAt - Date.now());
|
|
57
56
|
}
|
|
57
|
+
bucket.activeRequests += 1;
|
|
58
|
+
// Resolve the next item in the queue
|
|
59
|
+
bucket.waiting.shift()?.();
|
|
58
60
|
}
|
|
59
61
|
// Mark as false so next pending request can be triggered by new loop.
|
|
60
62
|
bucket.processing = false;
|
|
61
63
|
},
|
|
62
64
|
handleCompletedRequest: function(code, sharedScope) {
|
|
63
65
|
// Since request is complete, we can remove one from requested.
|
|
64
|
-
bucket.
|
|
66
|
+
bucket.activeRequests -= 1;
|
|
65
67
|
// Since it is as a valid request, we don't need to do anything
|
|
66
68
|
if (!bucket.errorStatuses.includes(code)) return;
|
|
67
69
|
// Shared scope is not considered invalid
|
|
68
70
|
if (code === 429 && sharedScope) return;
|
|
69
71
|
// INVALID REQUEST WAS MADE
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// If a timeout was not started, start a timeout to reset this bucket
|
|
73
|
-
if (bucket.timeoutId === undefined) {
|
|
74
|
-
bucket.timeoutId = setTimeout(()=>{
|
|
75
|
-
bucket.current = 0;
|
|
76
|
-
bucket.timeoutId = undefined;
|
|
77
|
-
}, bucket.interval);
|
|
72
|
+
if (bucket.resetAt === undefined) {
|
|
73
|
+
bucket.resetAt = Date.now() + bucket.interval;
|
|
78
74
|
}
|
|
75
|
+
bucket.invalidRequests += 1;
|
|
76
|
+
logger.warn(`[InvalidBucket] an invalid request was made. Increasing invalidRequests count to ${bucket.invalidRequests}`);
|
|
79
77
|
}
|
|
80
78
|
};
|
|
81
79
|
return bucket;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/invalidBucket.ts"],"sourcesContent":["import { delay, logger } from '@discordeno/utils'\n\n/**\n * A invalid request bucket is used in a similar manner as a leaky bucket but a invalid request bucket can be refilled as needed.\n * It's purpose is to make sure the bot does not hit the limit to getting a 1 hr ban.\n *\n * @param options The options used to configure this bucket.\n * @returns RefillingBucket\n */\nexport function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket {\n const bucket: InvalidRequestBucket = {\n
|
|
1
|
+
{"version":3,"sources":["../src/invalidBucket.ts"],"sourcesContent":["import { delay, logger } from '@discordeno/utils'\n\n/**\n * A invalid request bucket is used in a similar manner as a leaky bucket but a invalid request bucket can be refilled as needed.\n * It's purpose is to make sure the bot does not hit the limit to getting a 1 hr ban.\n *\n * @param options The options used to configure this bucket.\n * @returns RefillingBucket\n */\nexport function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket {\n const bucket: InvalidRequestBucket = {\n invalidRequests: options.current ?? 0,\n max: options.max ?? 10000,\n interval: options.interval ?? 600_000, // 10 minutes\n resetAt: options.resetAt,\n safety: options.safety ?? 1,\n errorStatuses: options.errorStatuses ?? [401, 403, 429],\n activeRequests: options.requested ?? 0,\n processing: false,\n\n waiting: [],\n\n requestsAllowed: function () {\n if (bucket.resetAt !== undefined && Date.now() >= bucket.resetAt) {\n bucket.invalidRequests = 0\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n return bucket.max - bucket.invalidRequests - bucket.activeRequests - bucket.safety\n },\n\n isRequestAllowed: function () {\n return bucket.requestsAllowed() > 0\n },\n\n waitUntilRequestAvailable: async function () {\n // eslint-disable-next-line no-async-promise-executor\n return await new Promise(async (resolve) => {\n // If whatever amount of requests is left is more than the safety margin, allow the request\n if (bucket.isRequestAllowed()) {\n bucket.activeRequests += 1\n resolve()\n } else {\n bucket.waiting.push(resolve)\n await bucket.processWaiting()\n }\n })\n },\n\n processWaiting: async function () {\n // If already processing, that loop will handle all waiting requests.\n if (bucket.processing) return\n\n // Mark as processing so other loops don't start\n bucket.processing = true\n\n while (bucket.waiting.length > 0) {\n logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} pending requests to be made. ${JSON.stringify(bucket)}`)\n\n if (!bucket.isRequestAllowed() && bucket.resetAt !== undefined) {\n logger.warn(`[InvalidBucket] processing waiting queue is now paused until more requests are available. ${bucket.waiting.length} pending requests. ${JSON.stringify(bucket)}`)\n await delay(bucket.resetAt - Date.now())\n }\n\n bucket.activeRequests += 1\n // Resolve the next item in the queue\n bucket.waiting.shift()?.()\n }\n\n // Mark as false so next pending request can be triggered by new loop.\n bucket.processing = false\n },\n\n handleCompletedRequest: function (code, sharedScope) {\n // Since request is complete, we can remove one from requested.\n bucket.activeRequests -= 1\n // Since it is as a valid request, we don't need to do anything\n if (!bucket.errorStatuses.includes(code)) return\n // Shared scope is not considered invalid\n if (code === 429 && sharedScope) return\n\n // INVALID REQUEST WAS MADE\n if (bucket.resetAt === undefined) {\n bucket.resetAt = Date.now() + bucket.interval\n }\n\n bucket.invalidRequests += 1\n logger.warn(`[InvalidBucket] an invalid request was made. Increasing invalidRequests count to ${bucket.invalidRequests}`)\n },\n }\n\n return bucket\n}\n\nexport interface InvalidRequestBucketOptions {\n /** current invalid amount */\n current?: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max?: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval?: number\n /** When the timeout for the bucket has started at. */\n resetAt?: number\n /** how safe to be from max. Defaults to 1 */\n safety?: number\n /** The request statuses that count as an invalid request. */\n errorStatuses?: number[]\n /** The amount of requests that were requested from this bucket. */\n requested?: number\n}\n\nexport interface InvalidRequestBucket {\n /** current invalid amount */\n invalidRequests: number\n /** max invalid requests allowed until ban. Defaults to 10,000 */\n max: number\n /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */\n interval: number\n /** When the timeout for this bucket has started at. */\n resetAt: number | undefined\n /** how safe to be from max. Defaults to 1 */\n safety: number\n /** The request statuses that count as an invalid request. */\n errorStatuses: number[]\n /** The amount of requests that were requested from this bucket. */\n activeRequests: number\n /** The requests that are currently pending. */\n waiting: Array<(value: void | PromiseLike<void>) => void>\n /** Whether or not the waiting queue is already processing. */\n processing: boolean\n\n /** Gives the number of requests that are currently allowed. */\n requestsAllowed: () => number\n /** Checks if a request is allowed at this time. */\n isRequestAllowed: () => boolean\n /** Waits until a request is available */\n waitUntilRequestAvailable: () => Promise<void>\n /** Begins processing the waiting queue of requests. */\n processWaiting: () => Promise<void>\n /** Handler for whenever a request is validated. This should update the requested values or trigger any other necessary stuff. */\n handleCompletedRequest: (code: number, sharedScope: boolean) => void\n}\n"],"names":["delay","logger","createInvalidRequestBucket","options","bucket","invalidRequests","current","max","interval","resetAt","safety","errorStatuses","activeRequests","requested","processing","waiting","requestsAllowed","undefined","Date","now","isRequestAllowed","waitUntilRequestAvailable","Promise","resolve","push","processWaiting","length","info","JSON","stringify","warn","shift","handleCompletedRequest","code","sharedScope","includes"],"mappings":"AAAA,SAASA,KAAK,EAAEC,MAAM,QAAQ,oBAAmB;AAEjD;;;;;;CAMC,GACD,OAAO,SAASC,2BAA2BC,OAAoC,EAAwB;IACrG,MAAMC,SAA+B;QACnCC,iBAAiBF,QAAQG,OAAO,IAAI;QACpCC,KAAKJ,QAAQI,GAAG,IAAI;QACpBC,UAAUL,QAAQK,QAAQ,IAAI;QAC9BC,SAASN,QAAQM,OAAO;QACxBC,QAAQP,QAAQO,MAAM,IAAI;QAC1BC,eAAeR,QAAQQ,aAAa,IAAI;YAAC;YAAK;YAAK;SAAI;QACvDC,gBAAgBT,QAAQU,SAAS,IAAI;QACrCC,YAAY,KAAK;QAEjBC,SAAS,EAAE;QAEXC,iBAAiB,WAAY;YAC3B,IAAIZ,OAAOK,OAAO,KAAKQ,aAAaC,KAAKC,GAAG,MAAMf,OAAOK,OAAO,EAAE;gBAChEL,OAAOC,eAAe,GAAG;gBACzBD,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAED,OAAOJ,OAAOG,GAAG,GAAGH,OAAOC,eAAe,GAAGD,OAAOQ,cAAc,GAAGR,OAAOM,MAAM;QACpF;QAEAU,kBAAkB,WAAY;YAC5B,OAAOhB,OAAOY,eAAe,KAAK;QACpC;QAEAK,2BAA2B,iBAAkB;YAC3C,qDAAqD;YACrD,OAAO,MAAM,IAAIC,QAAQ,OAAOC,UAAY;gBAC1C,2FAA2F;gBAC3F,IAAInB,OAAOgB,gBAAgB,IAAI;oBAC7BhB,OAAOQ,cAAc,IAAI;oBACzBW;gBACF,OAAO;oBACLnB,OAAOW,OAAO,CAACS,IAAI,CAACD;oBACpB,MAAMnB,OAAOqB,cAAc;gBAC7B,CAAC;YACH;QACF;QAEAA,gBAAgB,iBAAkB;YAChC,qEAAqE;YACrE,IAAIrB,OAAOU,UAAU,EAAE;YAEvB,gDAAgD;YAChDV,OAAOU,UAAU,GAAG,IAAI;YAExB,MAAOV,OAAOW,OAAO,CAACW,MAAM,GAAG,EAAG;gBAChCzB,OAAO0B,IAAI,CAAC,CAAC,6DAA6D,EAAEvB,OAAOW,OAAO,CAACW,MAAM,CAAC,8BAA8B,EAAEE,KAAKC,SAAS,CAACzB,QAAQ,CAAC;gBAE1J,IAAI,CAACA,OAAOgB,gBAAgB,MAAMhB,OAAOK,OAAO,KAAKQ,WAAW;oBAC9DhB,OAAO6B,IAAI,CAAC,CAAC,0FAA0F,EAAE1B,OAAOW,OAAO,CAACW,MAAM,CAAC,mBAAmB,EAAEE,KAAKC,SAAS,CAACzB,QAAQ,CAAC;oBAC5K,MAAMJ,MAAMI,OAAOK,OAAO,GAAGS,KAAKC,GAAG;gBACvC,CAAC;gBAEDf,OAAOQ,cAAc,IAAI;gBACzB,qCAAqC;gBACrCR,OAAOW,OAAO,CAACgB,KAAK;YACtB;YAEA,sEAAsE;YACtE3B,OAAOU,UAAU,GAAG,KAAK;QAC3B;QAEAkB,wBAAwB,SAAUC,IAAI,EAAEC,WAAW,EAAE;YACnD,+DAA+D;YAC/D9B,OAAOQ,cAAc,IAAI;YACzB,+DAA+D;YAC/D,IAAI,CAACR,OAAOO,aAAa,CAACwB,QAAQ,CAACF,OAAO;YAC1C,yCAAyC;YACzC,IAAIA,SAAS,OAAOC,aAAa;YAEjC,2BAA2B;YAC3B,IAAI9B,OAAOK,OAAO,KAAKQ,WAAW;gBAChCb,OAAOK,OAAO,GAAGS,KAAKC,GAAG,KAAKf,OAAOI,QAAQ;YAC/C,CAAC;YAEDJ,OAAOC,eAAe,IAAI;YAC1BJ,OAAO6B,IAAI,CAAC,CAAC,iFAAiF,EAAE1B,OAAOC,eAAe,CAAC,CAAC;QAC1H;IACF;IAEA,OAAOD;AACT,CAAC"}
|
package/dist/manager.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import type { CreateRestManagerOptions, RestManager } from './types.js';
|
|
2
|
+
export declare const DISCORD_API_VERSION = 10;
|
|
3
|
+
export declare const DISCORD_API_URL = "https://discord.com/api";
|
|
4
|
+
export declare const AUDIT_LOG_REASON_HEADER = "x-audit-log-reason";
|
|
5
|
+
export declare const RATE_LIMIT_REMAINING_HEADER = "x-ratelimit-remaining";
|
|
6
|
+
export declare const RATE_LIMIT_RESET_AFTER_HEADER = "x-ratelimit-reset-after";
|
|
7
|
+
export declare const RATE_LIMIT_GLOBAL_HEADER = "x-ratelimit-global";
|
|
8
|
+
export declare const RATE_LIMIT_BUCKET_HEADER = "x-ratelimit-bucket";
|
|
9
|
+
export declare const RATE_LIMIT_LIMIT_HEADER = "x-ratelimit-limit";
|
|
10
|
+
export declare const RATE_LIMIT_SCOPE_HEADER = "x-ratelimit-scope";
|
|
2
11
|
export declare function createRestManager(options: CreateRestManagerOptions): RestManager;
|
|
3
12
|
//# sourceMappingURL=manager.d.ts.map
|
package/dist/manager.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAA4B,wBAAwB,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAA4B,wBAAwB,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAA;AAKrH,eAAO,MAAM,mBAAmB,KAAK,CAAA;AACrC,eAAO,MAAM,eAAe,4BAA4B,CAAA;AAExD,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,2BAA2B,0BAA0B,CAAA;AAClE,eAAO,MAAM,6BAA6B,4BAA4B,CAAA;AACtE,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,wBAAwB,uBAAuB,CAAA;AAC5D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAC1D,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAE1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,WAAW,CAuqChF"}
|
package/dist/manager.js
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import {
|
|
2
|
-
import { calculateBits, camelize, camelToSnakeCase, delay, getBotIdFromToken, logger, processReactionString, urlToBase64 } from '@discordeno/utils';
|
|
3
|
-
import fetch from 'node-fetch';
|
|
1
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable no-const-assign */ import { calculateBits, camelToSnakeCase, camelize, delay, getBotIdFromToken, logger, processReactionString, urlToBase64 } from '@discordeno/utils';
|
|
4
2
|
import { createInvalidRequestBucket } from './invalidBucket.js';
|
|
5
3
|
import { Queue } from './queue.js';
|
|
4
|
+
import { InteractionResponseTypes } from '@discordeno/types';
|
|
6
5
|
import { createRoutes } from './routes.js';
|
|
7
6
|
// TODO: make dynamic based on package.json file
|
|
8
7
|
const version = '19.0.0-alpha.1';
|
|
8
|
+
export const DISCORD_API_VERSION = 10;
|
|
9
|
+
export const DISCORD_API_URL = 'https://discord.com/api';
|
|
10
|
+
export const AUDIT_LOG_REASON_HEADER = 'x-audit-log-reason';
|
|
11
|
+
export const RATE_LIMIT_REMAINING_HEADER = 'x-ratelimit-remaining';
|
|
12
|
+
export const RATE_LIMIT_RESET_AFTER_HEADER = 'x-ratelimit-reset-after';
|
|
13
|
+
export const RATE_LIMIT_GLOBAL_HEADER = 'x-ratelimit-global';
|
|
14
|
+
export const RATE_LIMIT_BUCKET_HEADER = 'x-ratelimit-bucket';
|
|
15
|
+
export const RATE_LIMIT_LIMIT_HEADER = 'x-ratelimit-limit';
|
|
16
|
+
export const RATE_LIMIT_SCOPE_HEADER = 'x-ratelimit-scope';
|
|
9
17
|
export function createRestManager(options) {
|
|
10
18
|
const applicationId = options.applicationId ? BigInt(options.applicationId) : options.token ? getBotIdFromToken(options.token) : undefined;
|
|
11
19
|
if (!applicationId) {
|
|
12
20
|
throw new Error('`applicationId` was not provided and was not able to extract the id from the bots token. Please explicitly pass `applicationId` to the rest manager.');
|
|
13
21
|
}
|
|
14
|
-
const baseUrl = options.proxy?.baseUrl ??
|
|
22
|
+
const baseUrl = options.proxy?.baseUrl ?? DISCORD_API_URL;
|
|
15
23
|
const rest = {
|
|
16
|
-
token: options.token,
|
|
17
24
|
applicationId,
|
|
18
|
-
|
|
25
|
+
authorization: options.proxy?.authorization,
|
|
19
26
|
baseUrl,
|
|
20
|
-
|
|
21
|
-
maxRetryCount: Infinity,
|
|
27
|
+
deleteQueueDelay: 60000,
|
|
22
28
|
globallyRateLimited: false,
|
|
29
|
+
invalidBucket: createInvalidRequestBucket({}),
|
|
30
|
+
isProxied: !baseUrl.startsWith(DISCORD_API_URL),
|
|
31
|
+
maxRetryCount: Infinity,
|
|
23
32
|
processingRateLimitedPaths: false,
|
|
24
|
-
deleteQueueDelay: 60000,
|
|
25
33
|
queues: new Map(),
|
|
26
34
|
rateLimitedPaths: new Map(),
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
token: options.token,
|
|
36
|
+
version: options.version ?? DISCORD_API_VERSION,
|
|
29
37
|
routes: createRoutes(),
|
|
30
38
|
checkRateLimits (url) {
|
|
31
39
|
const ratelimited = rest.rateLimitedPaths.get(url);
|
|
@@ -47,20 +55,27 @@ export function createRestManager(options) {
|
|
|
47
55
|
}
|
|
48
56
|
const newObj = {};
|
|
49
57
|
for (const key of Object.keys(obj)){
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
const value = obj[key];
|
|
59
|
+
// Some falsy values should be allowed like null or 0
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
switch(key){
|
|
62
|
+
case 'permissions':
|
|
63
|
+
case 'allow':
|
|
64
|
+
case 'deny':
|
|
65
|
+
newObj[key] = calculateBits(value);
|
|
66
|
+
continue;
|
|
67
|
+
case 'defaultMemberPermissions':
|
|
68
|
+
newObj.default_member_permissions = calculateBits(value);
|
|
69
|
+
continue;
|
|
70
|
+
case 'nameLocalizations':
|
|
71
|
+
newObj.name_localizations = value;
|
|
72
|
+
continue;
|
|
73
|
+
case 'descriptionLocalizations':
|
|
74
|
+
newObj.description_localizations = value;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
58
77
|
}
|
|
59
|
-
|
|
60
|
-
newObj.default_member_permissions = calculateBits(obj[key]);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(obj[key]);
|
|
78
|
+
newObj[camelToSnakeCase(key)] = rest.changeToDiscordFormat(value);
|
|
64
79
|
}
|
|
65
80
|
return newObj;
|
|
66
81
|
}
|
|
@@ -74,7 +89,7 @@ export function createRestManager(options) {
|
|
|
74
89
|
if (options?.unauthorized !== false) headers.authorization = `Bot ${rest.token}`;
|
|
75
90
|
// IF A REASON IS PROVIDED ENCODE IT IN HEADERS
|
|
76
91
|
if (options?.reason !== undefined) {
|
|
77
|
-
headers[
|
|
92
|
+
headers[AUDIT_LOG_REASON_HEADER] = encodeURIComponent(options?.reason);
|
|
78
93
|
}
|
|
79
94
|
let body;
|
|
80
95
|
// TODO: check if we need to add specific check for GET method
|
|
@@ -140,13 +155,13 @@ export function createRestManager(options) {
|
|
|
140
155
|
/** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ processHeaders (url, headers) {
|
|
141
156
|
let rateLimited = false;
|
|
142
157
|
// GET ALL NECESSARY HEADERS
|
|
143
|
-
const remaining = headers.get(
|
|
144
|
-
const retryAfter = headers.get(
|
|
158
|
+
const remaining = headers.get(RATE_LIMIT_REMAINING_HEADER);
|
|
159
|
+
const retryAfter = headers.get(RATE_LIMIT_RESET_AFTER_HEADER);
|
|
145
160
|
const reset = Date.now() + Number(retryAfter) * 1000;
|
|
146
|
-
const global = headers.get(
|
|
161
|
+
const global = headers.get(RATE_LIMIT_GLOBAL_HEADER);
|
|
147
162
|
// undefined override null needed for typings
|
|
148
|
-
const bucketId = headers.get(
|
|
149
|
-
const limit = headers.get(
|
|
163
|
+
const bucketId = headers.get(RATE_LIMIT_BUCKET_HEADER) ?? undefined;
|
|
164
|
+
const limit = headers.get(RATE_LIMIT_LIMIT_HEADER);
|
|
150
165
|
rest.queues.get(url)?.handleCompletedRequest({
|
|
151
166
|
remaining: remaining ? Number(remaining) : undefined,
|
|
152
167
|
interval: retryAfter ? Number(retryAfter) * 1000 : undefined,
|
|
@@ -210,8 +225,20 @@ export function createRestManager(options) {
|
|
|
210
225
|
authorization: 'Bot tokenhere'
|
|
211
226
|
}
|
|
212
227
|
});
|
|
213
|
-
const response = await fetch(url, payload)
|
|
228
|
+
const response = await fetch(url, payload).catch(async (error)=>{
|
|
229
|
+
logger.error(error);
|
|
230
|
+
// Mark request and completed
|
|
231
|
+
rest.invalidBucket.handleCompletedRequest(999, false);
|
|
232
|
+
options.reject({
|
|
233
|
+
ok: false,
|
|
234
|
+
status: 999,
|
|
235
|
+
error: 'Possible network or request shape issue occurred. If this is rare, its a network glitch. If it occurs a lot something is wrong.'
|
|
236
|
+
});
|
|
237
|
+
throw error;
|
|
238
|
+
});
|
|
214
239
|
logger.debug(`request fetched from ${url} with status ${response.status} & ${response.statusText}`);
|
|
240
|
+
// Mark request and completed
|
|
241
|
+
rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get(RATE_LIMIT_SCOPE_HEADER) === 'shared');
|
|
215
242
|
// Set the bucket id if it was available on the headers
|
|
216
243
|
const bucketId = rest.processHeaders(rest.simplifyUrl(options.route, options.method), response.headers);
|
|
217
244
|
if (bucketId) options.bucketId = bucketId;
|
|
@@ -238,9 +265,7 @@ export function createRestManager(options) {
|
|
|
238
265
|
return;
|
|
239
266
|
}
|
|
240
267
|
options.retryCount += 1;
|
|
241
|
-
|
|
242
|
-
rest.invalidBucket.handleCompletedRequest(response.status, response.headers.get('X-RateLimit-Scope') === 'shared');
|
|
243
|
-
const resetAfter = response.headers.get('x-ratelimit-reset-after');
|
|
268
|
+
const resetAfter = response.headers.get(RATE_LIMIT_RESET_AFTER_HEADER);
|
|
244
269
|
if (resetAfter) await delay(Number(resetAfter) * 1000);
|
|
245
270
|
// process the response to prevent mem leak
|
|
246
271
|
await response.arrayBuffer();
|
|
@@ -275,14 +300,6 @@ export function createRestManager(options) {
|
|
|
275
300
|
return parts.join('/');
|
|
276
301
|
},
|
|
277
302
|
async processRequest (request) {
|
|
278
|
-
const route = request.route.substring(request.route.indexOf('api/'));
|
|
279
|
-
const parts = route.split('/');
|
|
280
|
-
// Remove the api/
|
|
281
|
-
parts.shift();
|
|
282
|
-
// Removes the /v#/
|
|
283
|
-
if (parts[0]?.startsWith('v')) parts.shift();
|
|
284
|
-
// Set the full url to discord api in case it was recieved in a proxy rest
|
|
285
|
-
request.route = `${rest.baseUrl}/v${rest.version}/${parts.join('/')}`;
|
|
286
303
|
const url = rest.simplifyUrl(request.route, request.method);
|
|
287
304
|
if (request.runThroughQueue === false) {
|
|
288
305
|
await rest.sendRequest(request);
|
|
@@ -305,6 +322,11 @@ export function createRestManager(options) {
|
|
|
305
322
|
},
|
|
306
323
|
async makeRequest (method, route, options) {
|
|
307
324
|
if (rest.isProxied) {
|
|
325
|
+
if (rest.authorization !== undefined) {
|
|
326
|
+
options ??= {};
|
|
327
|
+
options.headers ??= {};
|
|
328
|
+
options.headers.authorization = rest.authorization;
|
|
329
|
+
}
|
|
308
330
|
const result = await fetch(`${rest.baseUrl}/v${rest.version}${route}`, rest.createRequestBody(method, options));
|
|
309
331
|
if (!result.ok) {
|
|
310
332
|
const err = await result.json().catch(()=>{});
|
|
@@ -1082,6 +1104,25 @@ export function createRestManager(options) {
|
|
|
1082
1104
|
return await rest.put(rest.routes.interactions.commands.guilds.all(rest.applicationId, guildId), {
|
|
1083
1105
|
body
|
|
1084
1106
|
});
|
|
1107
|
+
},
|
|
1108
|
+
preferSnakeCase (enabled) {
|
|
1109
|
+
const camelizer = enabled ? (x)=>x : camelize;
|
|
1110
|
+
rest.get = async (url, options)=>{
|
|
1111
|
+
return camelizer(await rest.makeRequest('GET', url, options));
|
|
1112
|
+
};
|
|
1113
|
+
rest.post = async (url, options)=>{
|
|
1114
|
+
return camelizer(await rest.makeRequest('POST', url, options));
|
|
1115
|
+
};
|
|
1116
|
+
rest.delete = async (url, options)=>{
|
|
1117
|
+
camelizer(await rest.makeRequest('DELETE', url, options));
|
|
1118
|
+
};
|
|
1119
|
+
rest.patch = async (url, options)=>{
|
|
1120
|
+
return camelizer(await rest.makeRequest('PATCH', url, options));
|
|
1121
|
+
};
|
|
1122
|
+
rest.put = async (url, options)=>{
|
|
1123
|
+
return camelizer(await rest.makeRequest('PUT', url, options));
|
|
1124
|
+
};
|
|
1125
|
+
return rest;
|
|
1085
1126
|
}
|
|
1086
1127
|
};
|
|
1087
1128
|
return rest;
|