From 952cfc0456b389b57b41fb38a2b2f2d4e5444d25 Mon Sep 17 00:00:00 2001 From: abalabahaha Date: Fri, 29 Jul 2016 05:37:43 +0900 Subject: [PATCH] Experimental active ratelimits (adapted from Eris) --- lib/Client/InternalClient.js | 585 +++++++++++++++++++---------------- lib/Structures/Server.js | 18 +- lib/Util/Bucket.js | 55 ++++ src/Client/InternalClient.js | 124 +++++--- src/Structures/Server.js | 14 +- src/Util/Bucket.js | 40 +++ src/Util/Equality.js | 3 +- 7 files changed, 511 insertions(+), 328 deletions(-) create mode 100644 lib/Util/Bucket.js create mode 100644 src/Util/Bucket.js diff --git a/lib/Client/InternalClient.js b/lib/Client/InternalClient.js index 9df109d80..dd3f265ea 100644 --- a/lib/Client/InternalClient.js +++ b/lib/Client/InternalClient.js @@ -26,6 +26,10 @@ var _querystring2 = _interopRequireDefault(_querystring); var _Constants = require("../Constants"); +var _UtilBucket = require("../Util/Bucket"); + +var _UtilBucket2 = _interopRequireDefault(_UtilBucket); + var _UtilCache = require("../Util/Cache"); var _UtilCache2 = _interopRequireDefault(_UtilCache); @@ -119,60 +123,94 @@ var InternalClient = (function () { } InternalClient.prototype.apiRequest = function apiRequest(method, url, useAuth, data, file) { - var _this = this, - _arguments = arguments; - - var endpoint = url.replace(/\/[0-9]+/g, "/:id"); - if (this.retryAfters[endpoint]) { - if (this.retryAfters[endpoint] < Date.now()) { - delete this.retryAfters[endpoint]; + var resolve, reject; + var promise = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + var buckets = []; + var match = url.match(/\/channels\/([0-9]+)\/messages(\/[0-9]+)?$/); + if (match) { + if (this.user.bot) { + if (method === "post" || method === "patch") { + if (this.private_channels.get("id", match[1])) { + buckets = ["bot:msg:dm", "bot:msg:global"]; + } else if (match[1] = this.channels.get("id", match[1])) { + buckets = ["bot:msg:guild:" + match[1].id, "bot:msg:global"]; + } + } else if (method === "del" && (match[1] = this.channels.get("id", match[1]))) { + buckets = ["dmsg:" + match[1].id]; + } } else { - return new Promise(function (resolve, reject) { - setTimeout(function () { - _this.apiRequest.apply(_this, _arguments).then(resolve)["catch"](reject); - }, _this.retryAfters[endpoint] - Date.now()); - }); + buckets = ["msg"]; + } + } else if (method === "patch") { + if (url === "/users/@me" && this.user && data.username && data.username !== this.user.username) { + buckets = ["username"]; + } else if (match = url.match(/\/guilds\/([0-9]+)\/members\/[0-9]+$/)) { + buckets = ["guild_member:" + match[1]]; + } else if (match = url.match(/\/guilds\/([0-9]+)\/members\/@me\/nick$/)) { + buckets = ["guild_member_nick:" + match[1]]; } } - var ret = _superagent2["default"][method](url); - if (useAuth) { - ret.set("authorization", this.token); - } - if (file) { - ret.attach("file", file.file, file.name); - if (data) { - for (var i in data) { - if (data[i] !== undefined) { - ret.field(i, data[i]); + + var self = this; + + var actualCall = function actualCall() { + var ret = _superagent2["default"][method](url); + if (useAuth) { + ret.set("authorization", self.token); + } + if (file) { + ret.attach("file", file.file, file.name); + if (data) { + for (var i in data) { + if (data[i] !== undefined) { + ret.field(i, data[i]); + } } } + } else if (data) { + ret.send(data); } - } else if (data) { - ret.send(data); - } - ret.set('User-Agent', this.userAgentInfo.full); - return new Promise(function (resolve, reject) { + ret.set('User-Agent', self.userAgentInfo.full); ret.end(function (error, data) { if (error) { - if (!_this.client.options.rateLimitAsError && error.response && error.response.error && error.response.error.status && error.response.error.status === 429) { - - if (data.headers["retry-after"] || data.headers["Retry-After"]) { - var toWait = data.headers["retry-after"] || data.headers["Retry-After"]; - if (!_this.retryAfters[endpoint]) _this.retryAfters[endpoint] = Date.now() + parseInt(toWait); - setTimeout(function () { - _this.apiRequest.apply(_this, _arguments).then(resolve)["catch"](reject); - }, _this.retryAfters[endpoint] - Date.now()); - } else { - return reject(error); - } - } else { - return reject(error); + if (data.status === 429) { + self.emit("debug", "Encountered 429 at " + url); } + reject(error); } else { resolve(data.body); } }); - }); + }; + var waitFor = 1; + var i = 0; + var done = function done() { + if (++i === waitFor) { + actualCall(); + } + }; + for (var _iterator = buckets, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var bucket = _ref; + + ++waitFor; + this.buckets[bucket].queue(done); + } + done(); + return promise; }; InternalClient.prototype.setup = function setup(discordClient) { @@ -212,6 +250,12 @@ var InternalClient = (function () { this.readyTime = null; this.messageAwaits = {}; this.retryAfters = {}; + this.buckets = { + "bot:msg:dm": new _UtilBucket2["default"](5, 5000), + "bot:msg:global": new _UtilBucket2["default"](50, 10000), + "msg": new _UtilBucket2["default"](10, 10000), + "username": new _UtilBucket2["default"](2, 3600000) + }; if (!this.tokenCacher) { this.tokenCacher = new _UtilTokenCacher2["default"](this.client); @@ -220,19 +264,19 @@ var InternalClient = (function () { }; InternalClient.prototype.cleanIntervals = function cleanIntervals() { - for (var _iterator = this.intervals.typing.concat(this.intervals.misc).concat(this.intervals.kai), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + for (var _iterator2 = this.intervals.typing.concat(this.intervals.misc).concat(this.intervals.kai), _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref2; - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref2 = _iterator2[_i2++]; } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref2 = _i2.value; } - var interval = _ref; + var interval = _ref2; if (interval) { clearInterval(interval); @@ -241,26 +285,26 @@ var InternalClient = (function () { }; InternalClient.prototype.disconnected = function disconnected() { - var _this2 = this; + var _this = this; var autoReconnect = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; this.cleanIntervals(); this.voiceConnections.forEach(function (vc) { - _this2.leaveVoiceChannel(vc); + _this.leaveVoiceChannel(vc); }); if (autoReconnect) { this.autoReconnectInterval = Math.min(this.autoReconnectInterval * (Math.random() + 1), 60000); setTimeout(function () { - if (!_this2.email && !_this2.token) { + if (!_this.email && !_this.token) { return; } // Check whether the email is set (if not, only a token has been used for login) - _this2.loginWithToken(_this2.token, _this2.email, _this2.password)["catch"](function () { - return _this2.disconnected(true); + _this.loginWithToken(_this.token, _this.email, _this.password)["catch"](function () { + return _this.disconnected(true); }); }, this.autoReconnectInterval); } @@ -271,7 +315,7 @@ var InternalClient = (function () { //def leaveVoiceChannel InternalClient.prototype.leaveVoiceChannel = function leaveVoiceChannel(chann) { - var _this3 = this; + var _this2 = this; if (this.user.bot) { var leave = function leave(connection) { @@ -291,7 +335,7 @@ var InternalClient = (function () { return Promise.reject(new Error("channel is not a voice channel!")); } - var connection = _this3.voiceConnections.get("voiceChannel", channel); + var connection = _this2.voiceConnections.get("voiceChannel", channel); if (!connection) { return Promise.reject(new Error("not connected to that voice channel")); } @@ -314,11 +358,11 @@ var InternalClient = (function () { //def awaitResponse InternalClient.prototype.awaitResponse = function awaitResponse(msg) { - var _this4 = this; + var _this3 = this; return new Promise(function (resolve, reject) { - msg = _this4.resolver.resolveMessage(msg); + msg = _this3.resolver.resolveMessage(msg); if (!msg) { reject(new Error("message undefined")); @@ -327,18 +371,18 @@ var InternalClient = (function () { var awaitID = msg.channel.id + msg.author.id; - if (!_this4.messageAwaits[awaitID]) { - _this4.messageAwaits[awaitID] = []; + if (!_this3.messageAwaits[awaitID]) { + _this3.messageAwaits[awaitID] = []; } - _this4.messageAwaits[awaitID].push(resolve); + _this3.messageAwaits[awaitID].push(resolve); }); }; //def joinVoiceChannel InternalClient.prototype.joinVoiceChannel = function joinVoiceChannel(chann) { - var _this5 = this; + var _this4 = this; return this.resolver.resolveChannel(chann).then(function (channel) { if (!channel) { @@ -350,7 +394,7 @@ var InternalClient = (function () { } var joinSendWS = function joinSendWS() { - _this5.sendWS({ + _this4.sendWS({ op: 4, d: { "guild_id": channel.server.id, @@ -363,7 +407,7 @@ var InternalClient = (function () { var joinVoice = function joinVoice() { return new Promise(function (resolve, reject) { - var session = _this5.sessionID, + var session = _this4.sessionID, token, server = channel.server, endpoint; @@ -376,8 +420,8 @@ var InternalClient = (function () { token = data.d.token; endpoint = data.d.endpoint; if (!token || !endpoint) return; - var chan = new _VoiceVoiceConnection2["default"](channel, _this5.client, session, token, server, endpoint); - _this5.voiceConnections.add(chan); + var chan = new _VoiceVoiceConnection2["default"](channel, _this4.client, session, token, server, endpoint); + _this4.voiceConnections.add(chan); chan.on("ready", function () { return resolve(chan); @@ -388,29 +432,29 @@ var InternalClient = (function () { if (timeout) { clearTimeout(timeout); } - _this5.client.removeListener("raw", check); + _this4.client.removeListener("raw", check); } }; timeout = setTimeout(function () { - _this5.client.removeListener("raw", check); + _this4.client.removeListener("raw", check); reject(new Error("No voice server details within 10 seconds")); }, 10000); - _this5.client.on("raw", check); + _this4.client.on("raw", check); joinSendWS(); }); }; - var existingServerConn = _this5.voiceConnections.get("server", channel.server); // same server connection + var existingServerConn = _this4.voiceConnections.get("server", channel.server); // same server connection if (existingServerConn) { joinSendWS(); // Just needs to update by sending via WS, movement in cache will be handled by global handler return Promise.resolve(existingServerConn); } - if (!_this5.user.bot && _this5.voiceConnections.length > 0) { + if (!_this4.user.bot && _this4.voiceConnections.length > 0) { // nonbot, one voiceconn only, just like last time just disconnect - return _this5.leaveVoiceChannel().then(joinVoice); + return _this4.leaveVoiceChannel().then(joinVoice); } return joinVoice(); @@ -462,7 +506,7 @@ var InternalClient = (function () { }; InternalClient.prototype.restartServerCreateTimeout = function restartServerCreateTimeout() { - var _this6 = this; + var _this5 = this; if (this.guildCreateTimeout) { clearTimeout(this.guildCreateTimeout); @@ -470,7 +514,7 @@ var InternalClient = (function () { } if (!this.readyTime) { this.guildCreateTimeout = setTimeout(function () { - _this6.checkReady(); + _this5.checkReady(); }, this.client.options.guildCreateTimeout); } }; @@ -478,7 +522,7 @@ var InternalClient = (function () { // def createServer InternalClient.prototype.createServer = function createServer(name) { - var _this7 = this; + var _this6 = this; var region = arguments.length <= 1 || arguments[1] === undefined ? "london" : arguments[1]; @@ -487,7 +531,7 @@ var InternalClient = (function () { return this.apiRequest('post', _Constants.Endpoints.SERVERS, true, { name: name, region: region }).then(function (res) { // valid server, wait until it is cached return waitFor(function () { - return _this7.servers.get("id", res.id); + return _this6.servers.get("id", res.id); }); }); }; @@ -495,7 +539,7 @@ var InternalClient = (function () { //def joinServer InternalClient.prototype.joinServer = function joinServer(invite) { - var _this8 = this; + var _this7 = this; invite = this.resolver.resolveInviteID(invite); if (!invite) { @@ -505,7 +549,7 @@ var InternalClient = (function () { return this.apiRequest("post", _Constants.Endpoints.INVITE(invite), true).then(function (res) { // valid server, wait until it is received via ws and cached return waitFor(function () { - return _this8.servers.get("id", res.guild.id); + return _this7.servers.get("id", res.guild.id); }); }); }; @@ -513,7 +557,7 @@ var InternalClient = (function () { //def updateServer InternalClient.prototype.updateServer = function updateServer(server, options) { - var _this9 = this; + var _this8 = this; var server = this.resolver.resolveServer(server); if (!server) { @@ -555,7 +599,7 @@ var InternalClient = (function () { return this.apiRequest("patch", _Constants.Endpoints.SERVER(server.id), true, options).then(function (res) { // wait until the name and region are updated return waitFor(function () { - return _this9.servers.get("name", res.name) ? _this9.servers.get("name", res.name).region === res.region ? _this9.servers.get("id", res.id) : false : false; + return _this8.servers.get("name", res.name) ? _this8.servers.get("name", res.name).region === res.region ? _this8.servers.get("id", res.id) : false : false; }); }); }; @@ -586,7 +630,7 @@ var InternalClient = (function () { // email and password are optional InternalClient.prototype.loginWithToken = function loginWithToken(token, email, password) { - var _this10 = this; + var _this9 = this; this.setup(); @@ -596,7 +640,7 @@ var InternalClient = (function () { this.password = password; return this.getGateway().then(function (url) { - _this10.createWS(url); + _this9.createWS(url); return token; }); }; @@ -604,14 +648,14 @@ var InternalClient = (function () { // def login InternalClient.prototype.login = function login(email, password) { - var _this11 = this; + var _this10 = this; var client = this.client; if (!this.tokenCacher.done) { return new Promise(function (resolve, reject) { setTimeout(function () { - _this11.login(email, password).then(resolve)["catch"](reject); + _this10.login(email, password).then(resolve)["catch"](reject); }, 20); }); } else { @@ -632,16 +676,16 @@ var InternalClient = (function () { email: email, password: password }).then(function (res) { - _this11.client.emit("debug", "direct API login, cached token was unavailable"); + _this10.client.emit("debug", "direct API login, cached token was unavailable"); var token = res.token; - _this11.tokenCacher.setToken(email, password, token); - return _this11.loginWithToken(token, email, password); + _this10.tokenCacher.setToken(email, password, token); + return _this10.loginWithToken(token, email, password); }, function (error) { - _this11.websocket = null; + _this10.websocket = null; throw error; })["catch"](function (error) { - _this11.websocket = null; - _this11.state = _ConnectionState2["default"].DISCONNECTED; + _this10.websocket = null; + _this10.state = _ConnectionState2["default"].DISCONNECTED; client.emit("disconnected"); throw error; }); @@ -650,21 +694,21 @@ var InternalClient = (function () { // def logout InternalClient.prototype.logout = function logout() { - var _this12 = this; + var _this11 = this; if (this.state === _ConnectionState2["default"].DISCONNECTED || this.state === _ConnectionState2["default"].IDLE) { return Promise.reject(new Error("Client is not logged in!")); } var disconnect = function disconnect() { - if (_this12.websocket) { - _this12.websocket.close(1000); - _this12.websocket = null; + if (_this11.websocket) { + _this11.websocket.close(1000); + _this11.websocket = null; } - _this12.token = null; - _this12.email = null; - _this12.password = null; - _this12.state = _ConnectionState2["default"].DISCONNECTED; + _this11.token = null; + _this11.email = null; + _this11.password = null; + _this11.state = _ConnectionState2["default"].DISCONNECTED; return Promise.resolve(); }; @@ -678,7 +722,7 @@ var InternalClient = (function () { // def startPM InternalClient.prototype.startPM = function startPM(resUser) { - var _this13 = this; + var _this12 = this; var user = this.resolver.resolveUser(resUser); if (!user) { @@ -688,27 +732,27 @@ var InternalClient = (function () { return this.apiRequest("post", _Constants.Endpoints.ME_CHANNELS, true, { recipient_id: user.id }).then(function (res) { - return _this13.private_channels.add(new _StructuresPMChannel2["default"](res, _this13.client)); + return _this12.private_channels.add(new _StructuresPMChannel2["default"](res, _this12.client)); }); }; // def getGateway InternalClient.prototype.getGateway = function getGateway() { - var _this14 = this; + var _this13 = this; if (this.gatewayURL) { return Promise.resolve(this.gatewayURL); } return this.apiRequest("get", _Constants.Endpoints.GATEWAY, true).then(function (res) { - return _this14.gatewayURL = res.url; + return _this13.gatewayURL = res.url; }); }; // def sendMessage InternalClient.prototype.sendMessage = function sendMessage(where, _content) { - var _this15 = this; + var _this14 = this; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; @@ -731,30 +775,30 @@ var InternalClient = (function () { } return this.resolver.resolveChannel(where).then(function (destination) { - var content = _this15.resolver.resolveString(_content); + var content = _this14.resolver.resolveString(_content); - if (_this15.client.options.disableEveryone || options.disableEveryone) { + if (_this14.client.options.disableEveryone || options.disableEveryone) { content = content.replace(/(@)(everyone|here)/g, "$1​$2"); } if (options.file) { - return _this15.resolver.resolveFile(options.file.file).then(function (file) { - return _this15.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(destination.id), true, { + return _this14.resolver.resolveFile(options.file.file).then(function (file) { + return _this14.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(destination.id), true, { content: content, tts: options.tts }, { name: options.file.name, file: file }).then(function (res) { - return destination.messages.add(new _StructuresMessage2["default"](res, destination, _this15.client)); + return destination.messages.add(new _StructuresMessage2["default"](res, destination, _this14.client)); }); }); } else { - return _this15.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(destination.id), true, { + return _this14.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(destination.id), true, { content: content, tts: options.tts }).then(function (res) { - return destination.messages.add(new _StructuresMessage2["default"](res, destination, _this15.client)); + return destination.messages.add(new _StructuresMessage2["default"](res, destination, _this14.client)); }); } }); @@ -763,7 +807,7 @@ var InternalClient = (function () { // def sendFile InternalClient.prototype.sendFile = function sendFile(where, _file, name, content) { - var _this16 = this; + var _this15 = this; if (!name) { if (_file instanceof String || typeof _file === "string") { @@ -786,12 +830,12 @@ var InternalClient = (function () { } return this.resolver.resolveChannel(where).then(function (channel) { - return _this16.resolver.resolveFile(_file).then(function (file) { - return _this16.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id), true, content, { + return _this15.resolver.resolveFile(_file).then(function (file) { + return _this15.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id), true, content, { name: name, file: file }).then(function (res) { - return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this16.client)); + return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this15.client)); }); }); }); @@ -800,7 +844,7 @@ var InternalClient = (function () { // def deleteMessage InternalClient.prototype.deleteMessage = function deleteMessage(_message) { - var _this17 = this; + var _this16 = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; @@ -811,7 +855,7 @@ var InternalClient = (function () { var chain = options.wait ? delay(options.wait) : Promise.resolve(); return chain.then(function () { - return _this17.apiRequest("del", _Constants.Endpoints.CHANNEL_MESSAGE(message.channel.id, message.id), true); + return _this16.apiRequest("del", _Constants.Endpoints.CHANNEL_MESSAGE(message.channel.id, message.id), true); }).then(function () { return message.channel.messages.remove(message); }); @@ -825,19 +869,19 @@ var InternalClient = (function () { var messages = []; var channel; - for (var _iterator2 = _messages, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; + for (var _iterator3 = _messages, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref3; - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref3 = _iterator3[_i3++]; } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref3 = _i3.value; } - var _message = _ref2; + var _message = _ref3; var message = this.resolver.resolveMessage(_message); if (!message) return Promise.reject(new Error("Something other than a message could not be resolved in the array...")); @@ -867,7 +911,7 @@ var InternalClient = (function () { // def updateMessage InternalClient.prototype.updateMessage = function updateMessage(msg, _content) { - var _this18 = this; + var _this17 = this; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; @@ -883,14 +927,14 @@ var InternalClient = (function () { content: content, tts: options.tts }).then(function (res) { - return message.channel.messages.update(message, new _StructuresMessage2["default"](res, message.channel, _this18.client)); + return message.channel.messages.update(message, new _StructuresMessage2["default"](res, message.channel, _this17.client)); }); }; // def getChannelLogs InternalClient.prototype.getChannelLogs = function getChannelLogs(_channel) { - var _this19 = this; + var _this18 = this; var limit = arguments.length <= 1 || arguments[1] === undefined ? 50 : arguments[1]; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; @@ -898,27 +942,27 @@ var InternalClient = (function () { return this.resolver.resolveChannel(_channel).then(function (channel) { var qsObject = { limit: limit }; if (options.before) { - var res = _this19.resolver.resolveMessage(options.before); + var res = _this18.resolver.resolveMessage(options.before); if (res) { qsObject.before = res.id; } } if (options.after) { - var res = _this19.resolver.resolveMessage(options.after); + var res = _this18.resolver.resolveMessage(options.after); if (res) { qsObject.after = res.id; } } if (options.around) { - var res = _this19.resolver.resolveMessage(options.around); + var res = _this18.resolver.resolveMessage(options.around); if (res) { qsObject.around = res.id; } } - return _this19.apiRequest("get", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id) + "?" + _querystring2["default"].stringify(qsObject), true).then(function (res) { + return _this18.apiRequest("get", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id) + "?" + _querystring2["default"].stringify(qsObject), true).then(function (res) { return res.map(function (msg) { - return channel.messages.add(new _StructuresMessage2["default"](msg, channel, _this19.client)); + return channel.messages.add(new _StructuresMessage2["default"](msg, channel, _this18.client)); }); }); }); @@ -927,10 +971,10 @@ var InternalClient = (function () { // def getMessage InternalClient.prototype.getMessage = function getMessage(_channel, messageID) { - var _this20 = this; + var _this19 = this; return this.resolver.resolveChannel(_channel).then(function (channel) { - if (!_this20.user.bot) { + if (!_this19.user.bot) { return Promise.reject(new Error("Only OAuth bot accounts can use this function")); } @@ -943,8 +987,8 @@ var InternalClient = (function () { return Promise.resolve(msg); } - return _this20.apiRequest("get", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id) + "/" + messageID, true).then(function (res) { - return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this20.client)); + return _this19.apiRequest("get", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id) + "/" + messageID, true).then(function (res) { + return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this19.client)); }); }); }; @@ -980,12 +1024,12 @@ var InternalClient = (function () { // def getPinnedMessages InternalClient.prototype.getPinnedMessages = function getPinnedMessages(_channel) { - var _this21 = this; + var _this20 = this; return this.resolver.resolveChannel(_channel).then(function (channel) { - return _this21.apiRequest("get", "" + _Constants.Endpoints.CHANNEL_PINS(channel.id), true).then(function (res) { + return _this20.apiRequest("get", "" + _Constants.Endpoints.CHANNEL_PINS(channel.id), true).then(function (res) { return res.map(function (msg) { - return channel.messages.add(new _StructuresMessage2["default"](msg, channel, _this21.client)); + return channel.messages.add(new _StructuresMessage2["default"](msg, channel, _this20.client)); }); }); }); @@ -994,13 +1038,13 @@ var InternalClient = (function () { // def getBans InternalClient.prototype.getBans = function getBans(server) { - var _this22 = this; + var _this21 = this; server = this.resolver.resolveServer(server); return this.apiRequest("get", _Constants.Endpoints.SERVER_BANS(server.id), true).then(function (res) { return res.map(function (ban) { - return _this22.users.add(new _StructuresUser2["default"](ban.user, _this22.client)); + return _this21.users.add(new _StructuresUser2["default"](ban.user, _this21.client)); }); }); }; @@ -1008,7 +1052,7 @@ var InternalClient = (function () { // def createChannel InternalClient.prototype.createChannel = function createChannel(server, name) { - var _this23 = this; + var _this22 = this; var type = arguments.length <= 2 || arguments[2] === undefined ? "text" : arguments[2]; @@ -1020,26 +1064,26 @@ var InternalClient = (function () { }).then(function (res) { var channel; if (res.type === "text") { - channel = new _StructuresTextChannel2["default"](res, _this23.client, server); + channel = new _StructuresTextChannel2["default"](res, _this22.client, server); } else { - channel = new _StructuresVoiceChannel2["default"](res, _this23.client, server); + channel = new _StructuresVoiceChannel2["default"](res, _this22.client, server); } - return server.channels.add(_this23.channels.add(channel)); + return server.channels.add(_this22.channels.add(channel)); }); }; // def deleteChannel InternalClient.prototype.deleteChannel = function deleteChannel(_channel) { - var _this24 = this; + var _this23 = this; return this.resolver.resolveChannel(_channel).then(function (channel) { - return _this24.apiRequest("del", _Constants.Endpoints.CHANNEL(channel.id), true).then(function () { + return _this23.apiRequest("del", _Constants.Endpoints.CHANNEL(channel.id), true).then(function () { if (channel.server) { channel.server.channels.remove(channel); - _this24.channels.remove(channel); + _this23.channels.remove(channel); } else { - _this24.private_channels.remove(channel); + _this23.private_channels.remove(channel); } }); }); @@ -1078,7 +1122,7 @@ var InternalClient = (function () { // def moveMember InternalClient.prototype.moveMember = function moveMember(user, channel) { - var _this25 = this; + var _this24 = this; user = this.resolver.resolveUser(user); return this.resolver.resolveChannel(channel).then(function (channel) { @@ -1088,7 +1132,7 @@ var InternalClient = (function () { if (channel.type !== "voice") { throw new Error("Can't moveMember into a non-voice channel"); } else { - return _this25.apiRequest("patch", _Constants.Endpoints.SERVER_MEMBERS(server.id) + "/" + user.id, true, { channel_id: channel.id }).then(function (res) { + return _this24.apiRequest("patch", _Constants.Endpoints.SERVER_MEMBERS(server.id) + "/" + user.id, true, { channel_id: channel.id }).then(function (res) { user.voiceChannel = channel; return res; }); @@ -1153,15 +1197,15 @@ var InternalClient = (function () { // def createRole InternalClient.prototype.createRole = function createRole(server, data) { - var _this26 = this; + var _this25 = this; server = this.resolver.resolveServer(server); return this.apiRequest("post", _Constants.Endpoints.SERVER_ROLES(server.id), true).then(function (res) { - var role = server.roles.add(new _StructuresRole2["default"](res, server, _this26.client)); + var role = server.roles.add(new _StructuresRole2["default"](res, server, _this25.client)); if (data) { - return _this26.updateRole(role, data); + return _this25.updateRole(role, data); } return role; }); @@ -1170,7 +1214,7 @@ var InternalClient = (function () { // def updateRole InternalClient.prototype.updateRole = function updateRole(role, data) { - var _this27 = this; + var _this26 = this; role = this.resolver.resolveRole(role); var server = this.resolver.resolveServer(role.server); @@ -1186,19 +1230,19 @@ var InternalClient = (function () { if (data.permissions) { newData.permissions = 0; - for (var _iterator3 = data.permissions, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; + for (var _iterator4 = data.permissions, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref4; - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref4 = _iterator4[_i4++]; } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref4 = _i4.value; } - var perm = _ref3; + var perm = _ref4; if (perm instanceof String || typeof perm === "string") { newData.permissions |= _Constants.Permissions[perm] || 0; @@ -1209,7 +1253,7 @@ var InternalClient = (function () { } return this.apiRequest("patch", _Constants.Endpoints.SERVER_ROLES(server.id) + "/" + role.id, true, newData).then(function (res) { - return server.roles.update(role, new _StructuresRole2["default"](res, server, _this27.client)); + return server.roles.update(role, new _StructuresRole2["default"](res, server, _this26.client)); }); }; @@ -1226,7 +1270,7 @@ var InternalClient = (function () { //def addMemberToRole InternalClient.prototype.addMemberToRole = function addMemberToRole(member, roles) { - var _this28 = this; + var _this27 = this; member = this.resolver.resolveUser(member); @@ -1243,7 +1287,7 @@ var InternalClient = (function () { } } else { roles = roles.map(function (r) { - return _this28.resolver.resolveRole(r); + return _this27.resolver.resolveRole(r); }); } @@ -1281,19 +1325,19 @@ var InternalClient = (function () { var roledata = role.server.rolesOf(member); if (roledata) { - for (var _iterator4 = roledata, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref4; + for (var _iterator5 = roledata, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref5; - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref4 = _iterator4[_i4++]; + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref5 = _iterator5[_i5++]; } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref4 = _i4.value; + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref5 = _i5.value; } - var r = _ref4; + var r = _ref5; if (r.id == role.id) { return r; @@ -1306,7 +1350,7 @@ var InternalClient = (function () { //def removeMemberFromRole InternalClient.prototype.removeMemberFromRole = function removeMemberFromRole(member, roles) { - var _this29 = this; + var _this28 = this; member = this.resolver.resolveUser(member); @@ -1323,7 +1367,7 @@ var InternalClient = (function () { } } else { roles = roles.map(function (r) { - return _this29.resolver.resolveRole(r); + return _this28.resolver.resolveRole(r); }); } @@ -1331,19 +1375,19 @@ var InternalClient = (function () { return r && r.id || r; }); - for (var _iterator5 = roles, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref5; + for (var _iterator6 = roles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref6; - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref5 = _iterator5[_i5++]; + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref6 = _iterator6[_i6++]; } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref5 = _i5.value; + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref6 = _i6.value; } - var role = _ref5; + var role = _ref6; if (!role.server.memberMap[member.id]) { return Promise.reject(new Error("member not in server")); @@ -1364,7 +1408,7 @@ var InternalClient = (function () { // def createInvite InternalClient.prototype.createInvite = function createInvite(chanServ, options) { - var _this30 = this; + var _this29 = this; return this.resolver.resolveChannel(chanServ).then(function (channel) { if (!options) { @@ -1378,8 +1422,8 @@ var InternalClient = (function () { options.xkcdpass = options.xkcd || false; } - return _this30.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true, options).then(function (res) { - return new _StructuresInvite2["default"](res, _this30.channels.get("id", res.channel.id), _this30.client); + return _this29.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true, options).then(function (res) { + return new _StructuresInvite2["default"](res, _this29.channels.get("id", res.channel.id), _this29.client); }); }); }; @@ -1397,7 +1441,7 @@ var InternalClient = (function () { //def getInvite InternalClient.prototype.getInvite = function getInvite(invite) { - var _this31 = this; + var _this30 = this; invite = this.resolver.resolveInviteID(invite); if (!invite) { @@ -1405,11 +1449,11 @@ var InternalClient = (function () { } return this.apiRequest("get", _Constants.Endpoints.INVITE(invite), true).then(function (res) { - if (!_this31.channels.has("id", res.channel.id)) { - return new _StructuresInvite2["default"](res, null, _this31.client); + if (!_this30.channels.has("id", res.channel.id)) { + return new _StructuresInvite2["default"](res, null, _this30.client); } - return _this31.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(res.channel.id), true, { validate: invite }).then(function (res2) { - return new _StructuresInvite2["default"](res2, _this31.channels.get("id", res.channel.id), _this31.client); + return _this30.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(res.channel.id), true, { validate: invite }).then(function (res2) { + return new _StructuresInvite2["default"](res2, _this30.channels.get("id", res.channel.id), _this30.client); }); }); }; @@ -1417,22 +1461,22 @@ var InternalClient = (function () { //def getInvites InternalClient.prototype.getInvites = function getInvites(channel) { - var _this32 = this; + var _this31 = this; if (!(channel instanceof _StructuresChannel2["default"])) { var server = this.resolver.resolveServer(channel); if (server) { return this.apiRequest("get", _Constants.Endpoints.SERVER_INVITES(server.id), true).then(function (res) { return res.map(function (data) { - return new _StructuresInvite2["default"](data, _this32.channels.get("id", data.channel.id), _this32.client); + return new _StructuresInvite2["default"](data, _this31.channels.get("id", data.channel.id), _this31.client); }); }); } } return this.resolver.resolveChannel(channel).then(function (channel) { - return _this32.apiRequest("get", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true).then(function (res) { + return _this31.apiRequest("get", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true).then(function (res) { return res.map(function (data) { - return new _StructuresInvite2["default"](data, _this32.channels.get("id", data.channel.id), _this32.client); + return new _StructuresInvite2["default"](data, _this31.channels.get("id", data.channel.id), _this31.client); }); }); }); @@ -1441,7 +1485,7 @@ var InternalClient = (function () { //def overwritePermissions InternalClient.prototype.overwritePermissions = function overwritePermissions(channel, role, updated) { - var _this33 = this; + var _this32 = this; return this.resolver.resolveChannel(channel).then(function (channel) { if (!channel instanceof _StructuresServerChannel2["default"]) { @@ -1454,7 +1498,7 @@ var InternalClient = (function () { }; if (role instanceof String || typeof role === "string") { - role = _this33.resolver.resolveUser(role) || _this33.resolver.resolveRole(role); + role = _this32.resolver.resolveUser(role) || _this32.resolver.resolveRole(role); } if (role instanceof _StructuresUser2["default"]) { @@ -1487,7 +1531,7 @@ var InternalClient = (function () { } } - return _this33.apiRequest("put", _Constants.Endpoints.CHANNEL_PERMISSIONS(channel.id) + "/" + data.id, true, data); + return _this32.apiRequest("put", _Constants.Endpoints.CHANNEL_PERMISSIONS(channel.id) + "/" + data.id, true, data); }); }; @@ -1527,49 +1571,49 @@ var InternalClient = (function () { //def sendTyping InternalClient.prototype.sendTyping = function sendTyping(channel) { - var _this34 = this; + var _this33 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - return _this34.apiRequest("post", _Constants.Endpoints.CHANNEL(channel.id) + "/typing", true); + return _this33.apiRequest("post", _Constants.Endpoints.CHANNEL(channel.id) + "/typing", true); }); }; //def startTyping InternalClient.prototype.startTyping = function startTyping(channel) { - var _this35 = this; + var _this34 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - if (_this35.intervals.typing[channel.id]) { + if (_this34.intervals.typing[channel.id]) { // typing interval already exists, leave it alone throw new Error("Already typing in that channel"); } - _this35.intervals.typing[channel.id] = setInterval(function () { - return _this35.sendTyping(channel)["catch"](function (error) { - return _this35.emit("error", error); + _this34.intervals.typing[channel.id] = setInterval(function () { + return _this34.sendTyping(channel)["catch"](function (error) { + return _this34.emit("error", error); }); }, 4000); - return _this35.sendTyping(channel); + return _this34.sendTyping(channel); }); }; //def stopTyping InternalClient.prototype.stopTyping = function stopTyping(channel) { - var _this36 = this; + var _this35 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - if (!_this36.intervals.typing[channel.id]) { + if (!_this35.intervals.typing[channel.id]) { // typing interval doesn"t exist throw new Error("Not typing in that channel"); } - clearInterval(_this36.intervals.typing[channel.id]); - _this36.intervals.typing[channel.id] = false; + clearInterval(_this35.intervals.typing[channel.id]); + _this35.intervals.typing[channel.id] = false; }); }; @@ -1651,7 +1695,7 @@ var InternalClient = (function () { //def updateChannel InternalClient.prototype.updateChannel = function updateChannel(channel, data) { - var _this37 = this; + var _this36 = this; return this.resolver.resolveChannel(channel).then(function (channel) { if (!channel) { @@ -1682,7 +1726,7 @@ var InternalClient = (function () { data.bitrate *= 1000; // convert to bits before sending } - return _this37.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, data).then(function (res) { + return _this36.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, data).then(function (res) { channel.name = data.name; channel.topic = data.topic; channel.position = data.position; @@ -1739,7 +1783,7 @@ var InternalClient = (function () { }; InternalClient.prototype.createWS = function createWS(url) { - var _this38 = this; + var _this37 = this; var self = this; var client = self.client; @@ -1794,7 +1838,7 @@ var InternalClient = (function () { }if (event.code === 4006 || event.code === 4009) { err = new Error("Invalid session"); } else if (event.code === 4007) { - _this38.seq = 0; + _this37.seq = 0; err = new Error("Invalid sequence number"); } else if (event.code === 4008) { err = new Error("Gateway connection was ratelimited"); @@ -1805,14 +1849,14 @@ var InternalClient = (function () { client.emit("error", err); } } - self.disconnected(_this38.client.options.autoReconnect); + self.disconnected(_this37.client.options.autoReconnect); }; this.websocket.onerror = function (e) { client.emit("error", e); self.websocket = null; self.state = _ConnectionState2["default"].DISCONNECTED; - self.disconnected(_this38.client.options.autoReconnect); + self.disconnected(_this37.client.options.autoReconnect); }; this.websocket.onmessage = function (e) { @@ -1841,11 +1885,11 @@ var InternalClient = (function () { self.user = self.users.add(new _StructuresUser2["default"](data.user, client)); - _this38.forceFetchCount = {}; - _this38.forceFetchQueue = []; - _this38.forceFetchLength = 1; - _this38.autoReconnectInterval = 1000; - _this38.sessionID = data.session_id; + _this37.forceFetchCount = {}; + _this37.forceFetchQueue = []; + _this37.forceFetchLength = 1; + _this37.autoReconnectInterval = 1000; + _this37.sessionID = data.session_id; data.guilds.forEach(function (server) { if (!server.unavailable) { @@ -2004,26 +2048,7 @@ var InternalClient = (function () { if (!data.unavailable) { client.emit("serverDeleted", server); - for (var _iterator6 = server.channels, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref6; - - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref6 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref6 = _i6.value; - } - - var channel = _ref6; - - self.channels.remove(channel); - } - - self.servers.remove(server); - - for (var _iterator7 = server.members, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + for (var _iterator7 = server.channels, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { var _ref7; if (_isArray7) { @@ -2035,22 +2060,41 @@ var InternalClient = (function () { _ref7 = _i7.value; } - var user = _ref7; + var channel = _ref7; + + self.channels.remove(channel); + } + + self.servers.remove(server); + + for (var _iterator8 = server.members, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref8; + + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref8 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref8 = _i8.value; + } + + var user = _ref8; var found = false; - for (var _iterator8 = self.servers, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { - var _ref8; + for (var _iterator9 = self.servers, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref9; - if (_isArray8) { - if (_i8 >= _iterator8.length) break; - _ref8 = _iterator8[_i8++]; + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref9 = _iterator9[_i9++]; } else { - _i8 = _iterator8.next(); - if (_i8.done) break; - _ref8 = _i8.value; + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref9 = _i9.value; } - var s = _ref8; + var s = _ref9; if (s.members.get("id", user.id)) { found = true; @@ -2064,6 +2108,7 @@ var InternalClient = (function () { } else { client.emit("debug", "server was unavailable, could not update"); } + self.buckets["bot:msg:guild:" + packet.d.id] = self.buckets["dmsg:" + packet.d.id] = self.buckets["bdmsg:" + packet.d.id] = self.buckets["guild_member:" + packet.d.id] = self.buckets["guild_member_nick:" + packet.d.id] = undefined; } else { client.emit("warn", "server was deleted but it was not in the cache"); } @@ -2309,7 +2354,7 @@ var InternalClient = (function () { data.id = data.id || user.id; data.avatar = data.avatar || user.avatar; data.discriminator = data.discriminator || user.discriminator; - _this38.email = data.email || _this38.email; + _this37.email = data.email || _this37.email; var presenceUser = new _StructuresUser2["default"](data, client); @@ -2367,7 +2412,7 @@ var InternalClient = (function () { } break; case _Constants.PacketType.USER_NOTE_UPDATE: - if (_this38.user.bot) { + if (_this37.user.bot) { return; } var user = self.users.get("id", data.id); @@ -2429,19 +2474,19 @@ var InternalClient = (function () { var testtime = Date.now(); - for (var _iterator9 = data.members, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref9; + for (var _iterator10 = data.members, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref10; - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref9 = _iterator9[_i9++]; + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref10 = _iterator10[_i10++]; } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref9 = _i9.value; + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref10 = _i10.value; } - var user = _ref9; + var user = _ref10; server.memberMap[user.user.id] = { roles: user.roles, @@ -2471,7 +2516,7 @@ var InternalClient = (function () { break; case _Constants.PacketType.FRIEND_ADD: - if (_this38.user.bot) { + if (_this37.user.bot) { return; } if (data.type === 1) { @@ -2502,7 +2547,7 @@ var InternalClient = (function () { } break; case _Constants.PacketType.FRIEND_REMOVE: - if (_this38.user.bot) { + if (_this37.user.bot) { return; } var user = self.friends.get("id", data.id); diff --git a/lib/Structures/Server.js b/lib/Structures/Server.js index ff341b221..7512f7f63 100644 --- a/lib/Structures/Server.js +++ b/lib/Structures/Server.js @@ -15,6 +15,10 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } +var _UtilBucket = require("../Util/Bucket"); + +var _UtilBucket2 = _interopRequireDefault(_UtilBucket); + var _UtilEquality = require("../Util/Equality"); var _UtilEquality2 = _interopRequireDefault(_UtilEquality); @@ -55,13 +59,21 @@ var Server = (function (_Equality) { _Equality.call(this); - var self = this; this.client = client; + this.id = data.id; + + if (data.owner_id) { + // new server data + client.internal.buckets["bot:msg:guild:" + this.id] = new _UtilBucket2["default"](5, 5000); + client.internal.buckets["dmsg:" + this.id] = new _UtilBucket2["default"](5, 1000); + client.internal.buckets["bdmsg:" + this.id] = new _UtilBucket2["default"](1, 1000); + client.internal.buckets["guild_member:" + this.id] = new _UtilBucket2["default"](10, 10000); + client.internal.buckets["guild_member_nick:" + this.id] = new _UtilBucket2["default"](1, 1000); + } this.region = data.region; this.ownerID = data.owner_id || data.ownerID; this.name = data.name; - this.id = data.id; this.members = new _UtilCache2["default"](); this.channels = new _UtilCache2["default"](); this.roles = new _UtilCache2["default"](); @@ -72,8 +84,6 @@ var Server = (function (_Equality) { this.memberCount = data.member_count || data.memberCount; this.large = data.large || this.memberCount > 250; - var self = this; - if (data.roles instanceof _UtilCache2["default"]) { data.roles.forEach(function (role) { return _this.roles.add(role); diff --git a/lib/Util/Bucket.js b/lib/Util/Bucket.js new file mode 100644 index 000000000..745ae1fad --- /dev/null +++ b/lib/Util/Bucket.js @@ -0,0 +1,55 @@ +"use strict"; + +exports.__esModule = true; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Bucket = (function () { + // Adapted from Eris + + function Bucket(tokenLimit, interval) { + _classCallCheck(this, Bucket); + + this.tokenLimit = tokenLimit; + this.interval = interval; + this.extraTime = 500; + this.lastReset = this.tokens = this.lastSend = 0; + this._queue = []; + } + + Bucket.prototype.queue = function queue(func) { + this._queue.push(func); + this.check(); + }; + + Bucket.prototype.check = function check() { + var _this = this; + + if (this.timeout || this._queue.length === 0) { + return; + } + if (this.lastReset + this.interval + this.extraTime < Date.now()) { + this.lastReset = Date.now(); + this.tokens = Math.max(0, this.tokens - this.tokenLimit); + } + + var val; + while (this._queue.length > 0 && this.tokens < this.tokenLimit) { + this.tokens++; + this._queue.shift()(); + this.lastSend = Date.now(); + } + + if (this._queue.length > 0 && !this.timeout) { + this.timeout = setTimeout(function () { + _this.timeout = null; + _this.check(); + }, this.tokens < this.tokenLimit ? 1 : Math.max(0, this.lastReset + this.interval + this.extraTime - Date.now())); + } + }; + + return Bucket; +})(); + +exports["default"] = Bucket; +module.exports = exports["default"]; diff --git a/src/Client/InternalClient.js b/src/Client/InternalClient.js index a111b3eb4..580f9cabf 100755 --- a/src/Client/InternalClient.js +++ b/src/Client/InternalClient.js @@ -7,6 +7,7 @@ import qs from "querystring"; import {Endpoints, PacketType, Permissions} from "../Constants"; +import Bucket from "../Util/Bucket"; import Cache from "../Util/Cache"; import Resolver from "./Resolver/Resolver"; @@ -52,64 +53,81 @@ export default class InternalClient { } apiRequest(method, url, useAuth, data, file) { - var endpoint = url.replace(/\/[0-9]+/g, "/:id"); - if (this.retryAfters[endpoint]) { - if (this.retryAfters[endpoint] < Date.now()) { - delete this.retryAfters[endpoint]; - } else { - return new Promise((resolve, reject) => { - setTimeout(() => { - this.apiRequest.apply(this, arguments).then(resolve).catch(reject); - }, this.retryAfters[endpoint] - Date.now()); - }); + var resolve, reject; + var promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }) + var buckets = []; + var match = url.match(/\/channels\/([0-9]+)\/messages(\/[0-9]+)?$/); + if(match) { + if(this.user.bot) { + if(method === "post" || method === "patch") { + if(this.private_channels.get("id", match[1])) { + buckets = ["bot:msg:dm", "bot:msg:global"]; + } else if((match[1] = this.channels.get("id", match[1]))) { + buckets = ["bot:msg:guild:" + match[1].id, "bot:msg:global"]; + } + } else if(method === "del" && (match[1] = this.channels.get("id", match[1]))) { + buckets = ["dmsg:" + match[1].id]; + } + } else { + buckets = ["msg"]; + } + } else if(method === "patch") { + if(url === "/users/@me" && this.user && data.username && data.username !== this.user.username) { + buckets = ["username"]; + } else if((match = url.match(/\/guilds\/([0-9]+)\/members\/[0-9]+$/))) { + buckets = ["guild_member:" + match[1]]; + } else if((match = url.match(/\/guilds\/([0-9]+)\/members\/@me\/nick$/))) { + buckets = ["guild_member_nick:" + match[1]]; + } + } + + var self = this; + + var actualCall = function() { + var ret = request[method](url); + if (useAuth) { + ret.set("authorization", self.token); } - } - let ret = request[method](url); - if (useAuth) { - ret.set("authorization", this.token); - } - if (file) { - ret.attach("file", file.file, file.name); - if (data) { - for (var i in data) { - if (data[i] !== undefined) { - ret.field(i, data[i]); + if (file) { + ret.attach("file", file.file, file.name); + if (data) { + for (var i in data) { + if (data[i] !== undefined) { + ret.field(i, data[i]); + } } } + } else if (data) { + ret.send(data); } - } else if (data) { - ret.send(data); - } - ret.set('User-Agent', this.userAgentInfo.full); - return new Promise((resolve, reject) => { + ret.set('User-Agent', self.userAgentInfo.full); ret.end((error, data) => { if (error) { - if (!this.client.options.rateLimitAsError && - error.response && - error.response.error && - error.response.error.status - && error.response.error.status === 429 - ) { - - if (data.headers["retry-after"] || data.headers["Retry-After"]) { - var toWait = data.headers["retry-after"] || data.headers["Retry-After"]; - if (!this.retryAfters[endpoint]) - this.retryAfters[endpoint] = Date.now() + parseInt(toWait); - setTimeout(() => { - this.apiRequest.apply(this, arguments).then(resolve).catch(reject); - }, this.retryAfters[endpoint] - Date.now()); - } else { - return reject(error); - } - - } else { - return reject(error); + if(data.status === 429) { + self.emit("debug", "Encountered 429 at " + url) } + reject(error); } else { resolve(data.body); } }); - }); + }; + var waitFor = 1; + var i = 0; + var done = function() { + if(++i === waitFor) { + actualCall(); + } + }; + for(var bucket of buckets) { + ++waitFor; + this.buckets[bucket].queue(done); + } + done(); + return promise; } setup(discordClient) { @@ -148,7 +166,12 @@ export default class InternalClient { this.resolver = new Resolver(this); this.readyTime = null; this.messageAwaits = {}; - this.retryAfters = {}; + this.buckets = { + "bot:msg:dm": new Bucket(5, 5000), + "bot:msg:global": new Bucket(50, 10000), + "msg": new Bucket(10, 10000), + "username": new Bucket(2, 3600000) + }; if (!this.tokenCacher) { this.tokenCacher = new TokenCacher(this.client); @@ -1807,6 +1830,11 @@ export default class InternalClient { } else { client.emit("debug", "server was unavailable, could not update"); } + self.buckets["bot:msg:guild:" + packet.d.id] = + self.buckets["dmsg:" + packet.d.id] = + self.buckets["bdmsg:" + packet.d.id] = + self.buckets["guild_member:" + packet.d.id] = + self.buckets["guild_member_nick:" + packet.d.id] = undefined; } else { client.emit("warn", "server was deleted but it was not in the cache"); } diff --git a/src/Structures/Server.js b/src/Structures/Server.js index a9f391748..3e92ab2ec 100644 --- a/src/Structures/Server.js +++ b/src/Structures/Server.js @@ -5,6 +5,7 @@ * @typedef {(string)} region */ +import Bucket from "../Util/Bucket"; import Equality from "../Util/Equality"; import {Endpoints} from "../Constants"; import Cache from "../Util/Cache"; @@ -24,13 +25,20 @@ export default class Server extends Equality { super(); - var self = this; this.client = client; + this.id = data.id; + + if(data.owner_id) { // new server data + client.internal.buckets["bot:msg:guild:" + this.id] = new Bucket(5, 5000); + client.internal.buckets["dmsg:" + this.id] = new Bucket(5, 1000); + client.internal.buckets["bdmsg:" + this.id] = new Bucket(1, 1000); + client.internal.buckets["guild_member:" + this.id] = new Bucket(10, 10000); + client.internal.buckets["guild_member_nick:" + this.id] = new Bucket(1, 1000); + } this.region = data.region; this.ownerID = data.owner_id || data.ownerID; this.name = data.name; - this.id = data.id; this.members = new Cache(); this.channels = new Cache(); this.roles = new Cache(); @@ -41,8 +49,6 @@ export default class Server extends Equality { this.memberCount = data.member_count || data.memberCount; this.large = data.large || this.memberCount > 250; - var self = this; - if (data.roles instanceof Cache) { data.roles.forEach((role) => this.roles.add(role)); } else { diff --git a/src/Util/Bucket.js b/src/Util/Bucket.js new file mode 100644 index 000000000..d3e6264ae --- /dev/null +++ b/src/Util/Bucket.js @@ -0,0 +1,40 @@ +"use strict"; + +export default class Bucket { // Adapted from Eris + constructor(tokenLimit, interval) { + this.tokenLimit = tokenLimit; + this.interval = interval; + this.extraTime = 500; + this.lastReset = this.tokens = this.lastSend = 0; + this._queue = []; + } + + queue(func) { + this._queue.push(func); + this.check(); + } + + check() { + if(this.timeout || this._queue.length === 0) { + return; + } + if(this.lastReset + this.interval + this.extraTime < Date.now()) { + this.lastReset = Date.now(); + this.tokens = Math.max(0, this.tokens - this.tokenLimit); + } + + var val; + while(this._queue.length > 0 && this.tokens < this.tokenLimit) { + this.tokens++; + this._queue.shift()(); + this.lastSend = Date.now(); + } + + if(this._queue.length > 0 && !this.timeout) { + this.timeout = setTimeout(() => { + this.timeout = null; + this.check(); + }, this.tokens < this.tokenLimit ? 1 : Math.max(0, this.lastReset + this.interval + this.extraTime - Date.now())); + } + } +} diff --git a/src/Util/Equality.js b/src/Util/Equality.js index 93bbc2a45..83ea530b5 100644 --- a/src/Util/Equality.js +++ b/src/Util/Equality.js @@ -9,9 +9,8 @@ Instead, use objectThatExtendsEquality.equals() */ -export default class Equality{ +export default class Equality { constructor(){ - } get eqDiscriminator(){