revbot.js 0.0.8 → 0.0.9

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/index.d.mts CHANGED
@@ -124,6 +124,7 @@ declare class BitField {
124
124
 
125
125
  declare class RestClient {
126
126
  private readonly client;
127
+ private rateLimitQueue;
127
128
  constructor(client: BaseClient);
128
129
  /**
129
130
  * Helper function to handle API requests.
@@ -176,6 +177,7 @@ declare class RestClient {
176
177
 
177
178
  declare class CDNClient {
178
179
  private readonly client;
180
+ private rateLimitQueue;
179
181
  constructor(client: BaseClient);
180
182
  /**
181
183
  * Helper function to handle API requests.
package/dist/index.d.ts CHANGED
@@ -124,6 +124,7 @@ declare class BitField {
124
124
 
125
125
  declare class RestClient {
126
126
  private readonly client;
127
+ private rateLimitQueue;
127
128
  constructor(client: BaseClient);
128
129
  /**
129
130
  * Helper function to handle API requests.
@@ -176,6 +177,7 @@ declare class RestClient {
176
177
 
177
178
  declare class CDNClient {
178
179
  private readonly client;
180
+ private rateLimitQueue;
179
181
  constructor(client: BaseClient);
180
182
  /**
181
183
  * Helper function to handle API requests.
package/dist/index.js CHANGED
@@ -3231,16 +3231,100 @@ var ServerMemberManager = class extends BaseManager {
3231
3231
  // src/client/baseClient.ts
3232
3232
  var import_node_events = require("events");
3233
3233
 
3234
- // src/rest/restClient.ts
3235
- var import_axios3 = __toESM(require("axios"));
3236
-
3237
3234
  // package.json
3238
- var version = "0.0.8";
3235
+ var version = "0.0.9";
3236
+
3237
+ // src/rest/restUtils/rateLimitQueue.ts
3238
+ var import_axios3 = __toESM(require("axios"));
3239
+ var RateLimitQueue = class {
3240
+ constructor() {
3241
+ this.bucketMap = /* @__PURE__ */ new Map();
3242
+ this.pathToBucket = /* @__PURE__ */ new Map();
3243
+ }
3244
+ request(config) {
3245
+ return __async(this, null, function* () {
3246
+ const path = config.url;
3247
+ const bucketId = this.pathToBucket.get(path);
3248
+ let bucket = bucketId ? this.bucketMap.get(bucketId) : void 0;
3249
+ if (bucket && bucket.remaining <= 0 && Date.now() < bucket.resetAfter) {
3250
+ return new Promise((resolve, reject) => {
3251
+ bucket.queue.push(() => __async(this, null, function* () {
3252
+ try {
3253
+ const res = yield this._doRequest(config, path);
3254
+ resolve(res);
3255
+ } catch (e) {
3256
+ reject(e);
3257
+ }
3258
+ }));
3259
+ });
3260
+ } else {
3261
+ return this._doRequest(config, path);
3262
+ }
3263
+ });
3264
+ }
3265
+ _doRequest(config, path) {
3266
+ return __async(this, null, function* () {
3267
+ const response = yield (0, import_axios3.default)(config);
3268
+ this._updateRateLimit(path, response);
3269
+ return response;
3270
+ });
3271
+ }
3272
+ _updateRateLimit(path, response) {
3273
+ const headers = response.headers;
3274
+ const limit = parseInt(headers["x-ratelimit-limit"]);
3275
+ const remaining = parseInt(headers["x-ratelimit-remaining"]);
3276
+ const resetAfter = parseFloat(headers["x-ratelimit-reset-after"]) * 1e3 + Date.now();
3277
+ const bucket = headers["x-ratelimit-bucket"];
3278
+ const resetIn = parseInt(headers["x-ratelimit-reset-after"]);
3279
+ if (!bucket) return;
3280
+ this.pathToBucket.set(path, bucket);
3281
+ let state = this.bucketMap.get(bucket);
3282
+ if (!state) {
3283
+ state = {
3284
+ limit,
3285
+ remaining,
3286
+ resetAfter,
3287
+ bucket,
3288
+ resetIn,
3289
+ queue: [],
3290
+ lastPath: path
3291
+ };
3292
+ this.bucketMap.set(bucket, state);
3293
+ } else {
3294
+ state.limit = limit;
3295
+ state.remaining = remaining;
3296
+ state.resetAfter = resetAfter;
3297
+ state.lastPath = path;
3298
+ }
3299
+ console.log(
3300
+ `Rate limit updated for bucket ${bucket} on path ${path}. Limit: ${limit}, Remaining: ${remaining}, Reset after: ${resetAfter} ms`
3301
+ );
3302
+ if (remaining <= 0) {
3303
+ console.warn(
3304
+ `Rate limit exceeded for bucket ${bucket} on path ${path}. Remaining: ${remaining}, Reset after: ${new Date(resetAfter).toISOString()}`
3305
+ );
3306
+ if (state.resetTimeout) clearTimeout(state.resetTimeout);
3307
+ const delay = resetIn;
3308
+ state.resetTimeout = setTimeout(() => {
3309
+ state.remaining = state.limit;
3310
+ state.resetTimeout = void 0;
3311
+ while (state.queue.length > 0 && state.remaining > 0) {
3312
+ const fn = state.queue.shift();
3313
+ if (fn) {
3314
+ state.remaining--;
3315
+ fn();
3316
+ }
3317
+ }
3318
+ }, delay);
3319
+ }
3320
+ }
3321
+ };
3239
3322
 
3240
3323
  // src/rest/restClient.ts
3241
3324
  var RestClient = class {
3242
3325
  constructor(client3) {
3243
3326
  this.client = client3;
3327
+ this.rateLimitQueue = new RateLimitQueue();
3244
3328
  }
3245
3329
  /**
3246
3330
  * Helper function to handle API requests.
@@ -3255,7 +3339,7 @@ var RestClient = class {
3255
3339
  try {
3256
3340
  if (!this.client.token) throw new Error("Token is required");
3257
3341
  const authHeader = this.client.bot ? "X-Bot-Token" : "X-Session-Token";
3258
- const config = {
3342
+ const config = __spreadProps(__spreadValues({}, {
3259
3343
  method,
3260
3344
  url: `${apiUrl}${url}`,
3261
3345
  params: query,
@@ -3264,8 +3348,10 @@ var RestClient = class {
3264
3348
  [authHeader]: this.client.token,
3265
3349
  "User-Agent": `RevBot.js/${version}`
3266
3350
  }
3267
- };
3268
- const response = yield (0, import_axios3.default)(config);
3351
+ }), {
3352
+ url: `${apiUrl}${url}`
3353
+ });
3354
+ const response = yield this.rateLimitQueue.request(config);
3269
3355
  return response.data;
3270
3356
  } catch (error) {
3271
3357
  console.error("API call failed:", error);
@@ -3334,10 +3420,10 @@ var RestClient = class {
3334
3420
  };
3335
3421
 
3336
3422
  // src/rest/CDNClient.ts
3337
- var import_axios4 = __toESM(require("axios"));
3338
3423
  var CDNClient = class {
3339
3424
  constructor(client3) {
3340
3425
  this.client = client3;
3426
+ this.rateLimitQueue = new RateLimitQueue();
3341
3427
  }
3342
3428
  /**
3343
3429
  * Helper function to handle API requests.
@@ -3352,7 +3438,7 @@ var CDNClient = class {
3352
3438
  try {
3353
3439
  if (!this.client.token) throw new Error("Token is required");
3354
3440
  const authHeader = this.client.bot ? "X-Bot-Token" : "X-Session-Token";
3355
- const config = {
3441
+ const config = __spreadProps(__spreadValues({}, {
3356
3442
  method,
3357
3443
  url: `${cdnUrl}${url}`,
3358
3444
  params: query,
@@ -3363,8 +3449,10 @@ var CDNClient = class {
3363
3449
  "Content-Type": "multipart/form-data",
3364
3450
  "User-Agent": `RevBot.js/${version}`
3365
3451
  }, data.getHeaders())
3366
- };
3367
- const response = yield (0, import_axios4.default)(config);
3452
+ }), {
3453
+ url: `${cdnUrl}${url}`
3454
+ });
3455
+ const response = yield this.rateLimitQueue.request(config);
3368
3456
  return response.data;
3369
3457
  } catch (error) {
3370
3458
  console.error("API call failed:", error);
package/dist/index.mjs CHANGED
@@ -3157,16 +3157,100 @@ var ServerMemberManager = class extends BaseManager {
3157
3157
  // src/client/baseClient.ts
3158
3158
  import { EventEmitter } from "node:events";
3159
3159
 
3160
- // src/rest/restClient.ts
3161
- import axios3 from "axios";
3162
-
3163
3160
  // package.json
3164
- var version = "0.0.8";
3161
+ var version = "0.0.9";
3162
+
3163
+ // src/rest/restUtils/rateLimitQueue.ts
3164
+ import axios3 from "axios";
3165
+ var RateLimitQueue = class {
3166
+ constructor() {
3167
+ this.bucketMap = /* @__PURE__ */ new Map();
3168
+ this.pathToBucket = /* @__PURE__ */ new Map();
3169
+ }
3170
+ request(config) {
3171
+ return __async(this, null, function* () {
3172
+ const path = config.url;
3173
+ const bucketId = this.pathToBucket.get(path);
3174
+ let bucket = bucketId ? this.bucketMap.get(bucketId) : void 0;
3175
+ if (bucket && bucket.remaining <= 0 && Date.now() < bucket.resetAfter) {
3176
+ return new Promise((resolve, reject) => {
3177
+ bucket.queue.push(() => __async(this, null, function* () {
3178
+ try {
3179
+ const res = yield this._doRequest(config, path);
3180
+ resolve(res);
3181
+ } catch (e) {
3182
+ reject(e);
3183
+ }
3184
+ }));
3185
+ });
3186
+ } else {
3187
+ return this._doRequest(config, path);
3188
+ }
3189
+ });
3190
+ }
3191
+ _doRequest(config, path) {
3192
+ return __async(this, null, function* () {
3193
+ const response = yield axios3(config);
3194
+ this._updateRateLimit(path, response);
3195
+ return response;
3196
+ });
3197
+ }
3198
+ _updateRateLimit(path, response) {
3199
+ const headers = response.headers;
3200
+ const limit = parseInt(headers["x-ratelimit-limit"]);
3201
+ const remaining = parseInt(headers["x-ratelimit-remaining"]);
3202
+ const resetAfter = parseFloat(headers["x-ratelimit-reset-after"]) * 1e3 + Date.now();
3203
+ const bucket = headers["x-ratelimit-bucket"];
3204
+ const resetIn = parseInt(headers["x-ratelimit-reset-after"]);
3205
+ if (!bucket) return;
3206
+ this.pathToBucket.set(path, bucket);
3207
+ let state = this.bucketMap.get(bucket);
3208
+ if (!state) {
3209
+ state = {
3210
+ limit,
3211
+ remaining,
3212
+ resetAfter,
3213
+ bucket,
3214
+ resetIn,
3215
+ queue: [],
3216
+ lastPath: path
3217
+ };
3218
+ this.bucketMap.set(bucket, state);
3219
+ } else {
3220
+ state.limit = limit;
3221
+ state.remaining = remaining;
3222
+ state.resetAfter = resetAfter;
3223
+ state.lastPath = path;
3224
+ }
3225
+ console.log(
3226
+ `Rate limit updated for bucket ${bucket} on path ${path}. Limit: ${limit}, Remaining: ${remaining}, Reset after: ${resetAfter} ms`
3227
+ );
3228
+ if (remaining <= 0) {
3229
+ console.warn(
3230
+ `Rate limit exceeded for bucket ${bucket} on path ${path}. Remaining: ${remaining}, Reset after: ${new Date(resetAfter).toISOString()}`
3231
+ );
3232
+ if (state.resetTimeout) clearTimeout(state.resetTimeout);
3233
+ const delay = resetIn;
3234
+ state.resetTimeout = setTimeout(() => {
3235
+ state.remaining = state.limit;
3236
+ state.resetTimeout = void 0;
3237
+ while (state.queue.length > 0 && state.remaining > 0) {
3238
+ const fn = state.queue.shift();
3239
+ if (fn) {
3240
+ state.remaining--;
3241
+ fn();
3242
+ }
3243
+ }
3244
+ }, delay);
3245
+ }
3246
+ }
3247
+ };
3165
3248
 
3166
3249
  // src/rest/restClient.ts
3167
3250
  var RestClient = class {
3168
3251
  constructor(client3) {
3169
3252
  this.client = client3;
3253
+ this.rateLimitQueue = new RateLimitQueue();
3170
3254
  }
3171
3255
  /**
3172
3256
  * Helper function to handle API requests.
@@ -3181,7 +3265,7 @@ var RestClient = class {
3181
3265
  try {
3182
3266
  if (!this.client.token) throw new Error("Token is required");
3183
3267
  const authHeader = this.client.bot ? "X-Bot-Token" : "X-Session-Token";
3184
- const config = {
3268
+ const config = __spreadProps(__spreadValues({}, {
3185
3269
  method,
3186
3270
  url: `${apiUrl}${url}`,
3187
3271
  params: query,
@@ -3190,8 +3274,10 @@ var RestClient = class {
3190
3274
  [authHeader]: this.client.token,
3191
3275
  "User-Agent": `RevBot.js/${version}`
3192
3276
  }
3193
- };
3194
- const response = yield axios3(config);
3277
+ }), {
3278
+ url: `${apiUrl}${url}`
3279
+ });
3280
+ const response = yield this.rateLimitQueue.request(config);
3195
3281
  return response.data;
3196
3282
  } catch (error) {
3197
3283
  console.error("API call failed:", error);
@@ -3260,10 +3346,10 @@ var RestClient = class {
3260
3346
  };
3261
3347
 
3262
3348
  // src/rest/CDNClient.ts
3263
- import axios4 from "axios";
3264
3349
  var CDNClient = class {
3265
3350
  constructor(client3) {
3266
3351
  this.client = client3;
3352
+ this.rateLimitQueue = new RateLimitQueue();
3267
3353
  }
3268
3354
  /**
3269
3355
  * Helper function to handle API requests.
@@ -3278,7 +3364,7 @@ var CDNClient = class {
3278
3364
  try {
3279
3365
  if (!this.client.token) throw new Error("Token is required");
3280
3366
  const authHeader = this.client.bot ? "X-Bot-Token" : "X-Session-Token";
3281
- const config = {
3367
+ const config = __spreadProps(__spreadValues({}, {
3282
3368
  method,
3283
3369
  url: `${cdnUrl}${url}`,
3284
3370
  params: query,
@@ -3289,8 +3375,10 @@ var CDNClient = class {
3289
3375
  "Content-Type": "multipart/form-data",
3290
3376
  "User-Agent": `RevBot.js/${version}`
3291
3377
  }, data.getHeaders())
3292
- };
3293
- const response = yield axios4(config);
3378
+ }), {
3379
+ url: `${cdnUrl}${url}`
3380
+ });
3381
+ const response = yield this.rateLimitQueue.request(config);
3294
3382
  return response.data;
3295
3383
  } catch (error) {
3296
3384
  console.error("API call failed:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revbot.js",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "A Revolt bot client used to interact with the revolt api for Node.js, written in TypeScript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",