|
/** |
|
* Constructor for RateLimit objects |
|
* @constructor |
|
* @param {number} [history=5] The size of the tick history |
|
*/ |
|
function RateLimit(history) { |
|
if (!(this instanceof RateLimit)) return new RateLimit(history); |
|
history = history || 5; |
|
|
|
/** |
|
* The time this RateLimit object was created |
|
* @type number |
|
*/ |
|
this.created = parseInt(new Date().getTime() / 1000); |
|
/** |
|
* Time of last tick |
|
* @type number |
|
*/ |
|
this.lasttick = 0; |
|
/** |
|
* Stores a bunch of ticks |
|
* @type array |
|
*/ |
|
this.ticklog = []; |
|
|
|
for (var i = 0; i < history; i++) { |
|
this.ticklog.push([0, {}]); |
|
} |
|
} |
|
RateLimit.prototype = { |
|
/** |
|
* Checks if the occurrences for this id are within the limit and if true, |
|
* does a tick and returns true, otherwise returns false. |
|
* @param id Any kind of identifier |
|
* @param {number} limit The max occurrences for this identifier |
|
* @param {number} [now] The time right now |
|
*/ |
|
tick: function(id, now) { |
|
if (!id) return; |
|
now = now || parseInt(new Date().getTime() / 1000) - this.created; |
|
this.lasttick = now; |
|
var index = now % this.ticklog.length; |
|
var entry = (this.ticklog[index] && |
|
this.ticklog[index][0] == now) |
|
? this.ticklog[index] |
|
: this.ticklog[index] = [now, {}]; |
|
var timestamp = entry[0], |
|
tickcount = entry[1]; |
|
tickcount[id] = tickcount[id] ? tickcount[id] + 1: 1; |
|
return; |
|
}, |
|
/** |
|
* Checks if the occurrences for this id are within the limit and if true, |
|
* does a tick and returns true, otherwise returns false. |
|
* @param id Any kind of identifier |
|
* @param {number} limit The max occurrences for this identifier |
|
* @param {number} [now] The time right now |
|
* @return {number} The number of ticks by a given id |
|
*/ |
|
count: function(id, now) { |
|
if (!id) return; |
|
now = now || parseInt(new Date().getTime() / 1000) - this.created; |
|
var count = 0; |
|
for (var i = 0; i < this.ticklog.length; i++) { |
|
var timestamp = this.ticklog[i][0], |
|
tickcount = this.ticklog[i][1]; |
|
if (now < timestamp || |
|
now - timestamp >= this.ticklog.length) { |
|
continue; |
|
} |
|
if (tickcount[id]) { |
|
count += tickcount[id]; |
|
} |
|
} |
|
return count; |
|
}, |
|
/** |
|
* Checks if the occurrences for this id are within the limit and if true, |
|
* does a tick and returns true, otherwise returns false. |
|
* @param id Any kind of identifier |
|
* @param {number} limit The max occurrences for this identifier |
|
* @param {number} [now] The time right now |
|
* @return {boolean} True if within the limit |
|
*/ |
|
execute: function(id, limit, now) { |
|
if (!id) return; |
|
if (!limit) return; |
|
now = now || parseInt(new Date().getTime() / 1000) - this.created; |
|
var success = this.count(id, now) < limit; |
|
if (success) { |
|
this.tick(id, now); |
|
} |
|
return success; |
|
} |
|
}; |