-
Notifications
You must be signed in to change notification settings - Fork 13
/
RateLimitManager.ts
89 lines (81 loc) · 3.16 KB
/
RateLimitManager.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
import { RateLimit } from './RateLimit';
import { Util } from '../util/Util';
interface NestedRateLimit { [descriptor: string]: NestedRateLimit | RateLimit }
/**
* Handles creation and retrieval of {@link RateLimit} objects for the given interval
* and a given set of descriptors. Similar to {@link RateLimiter} but not designed around
* Messages and Users. This simplifies the process of assigning ratelimits for any
* arbitrary process/task
*/
export class RateLimitManager
{
private readonly _ratelimits: NestedRateLimit;
public constructor()
{
this._ratelimits = {};
setInterval((async () => this._cleanup(this._ratelimits)) as any, 30e3);
}
/**
* Get a {@link RateLimit} object for the given target descriptors. This can be
* any arbitrary set of strings representing whatever you want. A good example
* would be a RateLimit for a User within a Guild with 5 uses per minute:
* ```
* <RateLimitManager>.get('5/1m', guild.id, user.id);
* ```
* Or if you wanted to limit something in a DM for the specific User to 10
* uses per 5 minutes:
* ```
* <RateLimitManager>.get('10/5m', user.id, 'DM');
* ```
* The possibilities are endless.<br><br>
*
* **Note:** The limit string counts as part of the descriptor. As such
* ```
* <RateLimitManager>.get('1/5m');
* ```
* is a valid descriptor, but keep in mind that every time you retrieve it, it
* will be the same RateLimit instance, so if you need unique RateLimits for the
* same limit/duration, you must create a unique descriptor
* @param {string} limit Ratelimit string matching the regex `\d+\/\d+[s|m|h|d]`<br>
* **Example:** `1/10m` to create ratelimits of 1 per 10 minutes
* @param {...string} descriptors RateLimit target descriptors
* @returns {RateLimit}
*/
public get(limit: string, ...descriptors: string[]): RateLimit
{
let rateLimit: RateLimit = Util.getNestedValue(this._ratelimits, [...descriptors, limit]);
if (rateLimit) return rateLimit;
rateLimit = new RateLimit(Util.parseRateLimit(limit));
Util.assignNestedValue(this._ratelimits, [...descriptors, limit], rateLimit);
return rateLimit;
}
/**
* Return the result of the {@link RateLimit#call} for the given
* descriptors. See {@link RateLimitManager#get} for details on
* fetching RateLimits via descriptors
* @param {string} limit Ratelimit string matching the regex `\d+\/\d+[s|m|h|d]`<br>
* **Example:** `1/10m` to create ratelimits of 1 per 10 minutes
* @param {...string} descriptors RateLimit target descriptors
* @returns {boolean}
*/
public call(limit: string, ...descriptors: string[]): boolean
{
return this.get(limit, ...descriptors).call();
}
/**
* Recursively clean up expired ratelimits within the given target object
* @private
*/
private async _cleanup(target: NestedRateLimit): Promise<void>
{
for (const key of Object.keys(target))
if (target[key] instanceof RateLimit)
{
const rateLimit: RateLimit = target[key] as RateLimit;
if (Date.now() - rateLimit.expires > rateLimit.duration + 10e3)
delete target[key];
}
else if (Object.keys(target[key]).length === 0) delete target[key];
else this._cleanup(target[key] as NestedRateLimit);
}
}