StrawbrryFlurry / watson

🚀 Fast, scalable framework for building Discord applications
MIT License
33 stars 1 forks source link

Discord rate-limiting #23

Open StrawbrryFlurry opened 3 years ago

StrawbrryFlurry commented 3 years ago

Discord's APIs are rate-limited. As a result, we'll have to figure out how we can make sure that every request can still be processed as expected by the user, even if we have to delay it for some time to work around the rate limits. - Discord rate-limits

Countermeasures

Add a global rate limit handler to reschedule calls that couldn't be completed due to rate limiting.

Provide a global rate limiter for scheduling calls that will likely get rate limited:

A rate limiter implementation could look something like this and would of curse also be customizable by the user.

/**
* Custom handlers of a rate limiter need to 
* implement the minimal features of this interface
*/
interface IRateLimitHandler {
  /**
  * Call the `cb` and check if it was rate limited.
  * If so retry the method call after `x` delay.
  *
  * The framework will also use this method
  * by default if a command or event route
  * call is being rate limited.
  * 
  * @returns A `Promise` that resolves once the
  * `cb` was successfully called.
  */
  retryOnLimit(cb: () => void): Promise<void>;
  /** 
  * When having to make a lot of API calls to 
  * delete channel messages or update a bunch 
  * of users you can use this method as a way
  * to schedule them. You provide an array of 
  * inputs which are then being used to execute 
  * the callback function. Items are processed 
  * every `x` interval or if we know for sure
  * we are not getting rate limited.
  * 
  * @returns A `Promise` that resolves once all
  * arguments were successfully processed.
  */
  scheduleBulk<T>(args: T[], (arg: T) => void): Promise<void>;
}

The Rate Limit handler should be available via DI and overwrote by using a custom provider.

matthew-e-brown commented 3 years ago

I assume Discord.js is wrapped all in one place as you go down lower and lower in levels of abstraction -- when you get to the point where you actually dispatch the commands to their API, why not do it with a queue system? You could just try it and then if it fails with a 429 you can just not remove it from the queue. If Discord gives a Retry-After header you can even use that.

StrawbrryFlurry commented 3 years ago

That sounds like a plan. To do this though we'd first need an internal client as a replacement for Discordjs and basically re-implement the features it has which is going to take a lot of time. For the start, I guess we'll just go with what's already there from Discordjs.