From a15b8e56c72d6d9fa7d4eafc51af2fc3a7f8fe56 Mon Sep 17 00:00:00 2001 From: Nicholas Tay Date: Tue, 5 Apr 2016 11:25:24 +1000 Subject: [PATCH] Multi voice connections across servers working --- lib/Client/Client.js | 22 +-- lib/Client/InternalClient.js | 336 ++++++++++++++++++++--------------- src/Client/Client.js | 14 +- src/Client/InternalClient.js | 124 ++++++++----- 4 files changed, 292 insertions(+), 204 deletions(-) diff --git a/lib/Client/Client.js b/lib/Client/Client.js index ad27a9121..506a6c02f 100644 --- a/lib/Client/Client.js +++ b/lib/Client/Client.js @@ -702,7 +702,7 @@ var Client = (function (_EventEmitter) { return this.internal.moveMember(user, channel).then(dataCallback(callback), errorCallback(callback)); }; - + // def muteMember Client.prototype.muteMember = function muteMember(user, server) { @@ -710,7 +710,7 @@ var Client = (function (_EventEmitter) { return this.internal.muteMember(user, server).then(dataCallback(callback), errorCallback(callback)); }; - + // def unmuteMember Client.prototype.unmuteMember = function unmuteMember(user, server) { @@ -718,7 +718,7 @@ var Client = (function (_EventEmitter) { return this.internal.unmuteMember(user, server).then(dataCallback(callback), errorCallback(callback)); }; - + // def deafenMember Client.prototype.deafenMember = function deafenMember(user, server) { @@ -726,7 +726,7 @@ var Client = (function (_EventEmitter) { return this.internal.deafenMember(user, server).then(dataCallback(callback), errorCallback(callback)); }; - + // def undeafenMember Client.prototype.undeafenMember = function undeafenMember(user, server) { @@ -979,10 +979,10 @@ var Client = (function (_EventEmitter) { // def leaveVoiceChannel - Client.prototype.leaveVoiceChannel = function leaveVoiceChannel() { - var callback = arguments.length <= 0 || arguments[0] === undefined ? function () /*err, {}*/{} : arguments[0]; + Client.prototype.leaveVoiceChannel = function leaveVoiceChannel(chann) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function () /*err, {}*/{} : arguments[1]; - return this.internal.leaveVoiceChannel().then(dataCallback(callback), errorCallback(callback)); + return this.internal.leaveVoiceChannel(chann).then(dataCallback(callback), errorCallback(callback)); }; // def addFriend @@ -1190,13 +1190,13 @@ var Client = (function (_EventEmitter) { } /** - * The active voice connection of the Client, or null if not applicable. Only available after `ready` event has been emitted. - * @type {VoiceConnection|null} the voice connection (if any). + * A cache of active voice connection of the Client, or null if not applicable. Only available after `ready` event has been emitted. + * @type {Cache} a Cache of Voice Connections */ }, { - key: "voiceConnection", + key: "voiceConnections", get: function get() { - return this.internal.voiceConnection; + return this.internal.voiceConnections; } /** diff --git a/lib/Client/InternalClient.js b/lib/Client/InternalClient.js index cec91151a..1d97be0bf 100644 --- a/lib/Client/InternalClient.js +++ b/lib/Client/InternalClient.js @@ -186,7 +186,7 @@ var InternalClient = (function () { misc: [] }; - this.voiceConnection = null; + this.voiceConnections = new _UtilCache2["default"](); this.resolver = new _ResolverResolver2["default"](this); this.readyTime = null; this.messageAwaits = {}; @@ -239,22 +239,57 @@ var InternalClient = (function () { //def leaveVoiceChannel - InternalClient.prototype.leaveVoiceChannel = function leaveVoiceChannel() { - if (this.voiceConnection) { - this.voiceConnection.destroy(); - this.voiceConnection = null; + InternalClient.prototype.leaveVoiceChannel = function leaveVoiceChannel(chann) { + var _this2 = this; + + if (this.user.bot) { + var leave = function leave(connection) { + return new Promise(function (resolve, reject) { + connection.destroy(); + _this2.voiceConnections.remove(connection); + resolve(); + }); + }; + + if (chann instanceof _StructuresVoiceChannel2["default"]) { + return this.resolver.resolveChannel(chann).then(function (channel) { + if (!channel) { + return Promise.reject(new Error("voice channel does not exist")); + } + + if (channel.type !== 'voice') { + return Promise.reject(new Error("channel is not a voice channel!")); + } + + var connection = _this2.voiceConnections.get("voiceChannel", channel); + if (!connection) { + return Promise.reject(new Error("not connected to that voice channel")); + } + return leave(connection); + }); + } else if (chann instanceof _VoiceVoiceConnection2["default"]) { + return leave(chann); + } else { + return Promise.reject(new Error("invalid voice channel/connection to leave")); + } + } else { + // preserve old functionality for non-bots + if (this.voiceConnections[0]) { + this.voiceConnections[0].destroy(); + this.voiceConnections = []; + } + return Promise.resolve(); } - return Promise.resolve(); }; //def awaitResponse InternalClient.prototype.awaitResponse = function awaitResponse(msg) { - var _this2 = this; + var _this3 = this; return new Promise(function (resolve, reject) { - msg = _this2.resolver.resolveMessage(msg); + msg = _this3.resolver.resolveMessage(msg); if (!msg) { reject(new Error("message undefined")); @@ -263,18 +298,18 @@ var InternalClient = (function () { var awaitID = msg.channel.id + msg.author.id; - if (!_this2.messageAwaits[awaitID]) { - _this2.messageAwaits[awaitID] = []; + if (!_this3.messageAwaits[awaitID]) { + _this3.messageAwaits[awaitID] = []; } - _this2.messageAwaits[awaitID].push(resolve); + _this3.messageAwaits[awaitID].push(resolve); }); }; //def joinVoiceChannel InternalClient.prototype.joinVoiceChannel = function joinVoiceChannel(chann) { - var _this3 = this; + var _this4 = this; return this.resolver.resolveChannel(chann).then(function (channel) { if (!channel) { @@ -285,51 +320,62 @@ var InternalClient = (function () { return Promise.reject(new Error("channel is not a voice channel!")); } - return _this3.leaveVoiceChannel().then(function () { - return new Promise(function (resolve, reject) { - var session, - token, - server = channel.server, - endpoint; + var joinVoice = new Promise(function (resolve, reject) { + var session, + token, + server = channel.server, + endpoint; - var check = function check(m) { - var data = JSON.parse(m); - if (data.t === "VOICE_STATE_UPDATE") { - session = data.d.session_id; - } else if (data.t === "VOICE_SERVER_UPDATE") { + var check = function check(m) { + var data = JSON.parse(m); + if (data.d.guild_id !== server.id) return; // ensure it is the right server + + if (data.t === "VOICE_STATE_UPDATE") { + session = data.d.session_id; + if (existingServerConn && data.d.channel_id !== existingServerConn.voiceChannel.id) { + existingServerConn.voiceChannel = channel; // existing connection to that server, just channel changed + } + } else if (data.t === "VOICE_SERVER_UPDATE") { token = data.d.token; endpoint = data.d.endpoint; - var chan = _this3.voiceConnection = new _VoiceVoiceConnection2["default"](channel, _this3.client, session, token, server, endpoint); + var chan = new _VoiceVoiceConnection2["default"](channel, _this4.client, session, token, server, endpoint); + _this4.voiceConnections.add(chan); chan.on("ready", function () { return resolve(chan); }); chan.on("error", reject); - _this3.client.emit("debug", "removed temporary voice websocket listeners"); - _this3.websocket.removeListener("message", check); + _this4.client.emit("debug", "removed temporary voice websocket listeners"); + _this4.websocket.removeListener("message", check); } - }; + }; - _this3.websocket.on("message", check); - _this3.sendWS({ - op: 4, - d: { - "guild_id": server.id, - "channel_id": channel.id, - "self_mute": false, - "self_deaf": false - } - }); + _this4.websocket.on("message", check); + _this4.sendWS({ + op: 4, + d: { + "guild_id": server.id, + "channel_id": channel.id, + "self_mute": false, + "self_deaf": false + } }); }); + + if (!_this4.user.bot && _this4.voiceConnections.length > 0) // nonbot, one voiceconn only, just like last time just disconnect + return _this4.leaveVoiceChannel(_this4.voiceConnections[0]).then(joinVoice); + + var existingServerConn = _this4.voiceConnections.get("server", channel.server); // same server connection + + return joinVoice; }); }; // def forceFetchUsers InternalClient.prototype.forceFetchUsers = function forceFetchUsers() { - var _this4 = this; + var _this5 = this; this.sendWS({ op: 8, @@ -366,7 +412,7 @@ var InternalClient = (function () { if (!server) { resolve(); } else { - _this4.chunkloaderCallback = resolve; + _this5.chunkloaderCallback = resolve; } }); }; @@ -374,7 +420,7 @@ var InternalClient = (function () { // def createServer InternalClient.prototype.createServer = function createServer(name) { - var _this5 = this; + var _this6 = this; var region = arguments.length <= 1 || arguments[1] === undefined ? "london" : arguments[1]; @@ -383,7 +429,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 _this5.servers.get("id", res.id); + return _this6.servers.get("id", res.id); }); }); }; @@ -391,7 +437,7 @@ var InternalClient = (function () { //def joinServer InternalClient.prototype.joinServer = function joinServer(invite) { - var _this6 = this; + var _this7 = this; invite = this.resolver.resolveInviteID(invite); if (!invite) { @@ -401,7 +447,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 _this6.servers.get("id", res.guild.id); + return _this7.servers.get("id", res.guild.id); }); }); }; @@ -409,7 +455,7 @@ var InternalClient = (function () { //def updateServer InternalClient.prototype.updateServer = function updateServer(server, name, region) { - var _this7 = this; + var _this8 = this; var server = this.resolver.resolveServer(server); if (!server) { @@ -419,7 +465,7 @@ var InternalClient = (function () { return this.apiRequest("patch", _Constants.Endpoints.SERVER(server.id), true, { name: name || server.name, region: region || server.region }).then(function (res) { // wait until the name and region are updated return waitFor(function () { - return _this7.servers.get("name", res.name) ? _this7.servers.get("name", res.name).region === res.region ? _this7.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; }); }); }; @@ -427,7 +473,7 @@ var InternalClient = (function () { //def leaveServer InternalClient.prototype.leaveServer = function leaveServer(srv) { - var _this8 = this; + var _this9 = this; var server = this.resolver.resolveServer(srv); if (!server) { @@ -450,17 +496,17 @@ var InternalClient = (function () { var chan = _ref3; - _this8.channels.remove(chan); + _this9.channels.remove(chan); } // remove server - _this8.servers.remove(server); + _this9.servers.remove(server); }); }; //def deleteServer InternalClient.prototype.deleteServer = function deleteServer(srv) { - var _this9 = this; + var _this10 = this; var server = this.resolver.resolveServer(srv); if (!server) { @@ -483,10 +529,10 @@ var InternalClient = (function () { var chan = _ref4; - _this9.channels.remove(chan); + _this10.channels.remove(chan); } // remove server - _this9.servers.remove(server); + _this10.servers.remove(server); }); }; @@ -494,7 +540,7 @@ var InternalClient = (function () { // email and password are optional InternalClient.prototype.loginWithToken = function loginWithToken(token, email, password) { - var _this10 = this; + var _this11 = this; this.state = _ConnectionState2["default"].LOGGED_IN; this.token = token; @@ -502,7 +548,7 @@ var InternalClient = (function () { this.password = password; return this.getGateway().then(function (url) { - _this10.createWS(url); + _this11.createWS(url); return token; }); }; @@ -510,14 +556,14 @@ var InternalClient = (function () { // def login InternalClient.prototype.login = function login(email, password) { - var _this11 = this; + var _this12 = 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); + _this12.login(email, password).then(resolve)["catch"](reject); }, 20); }); } else { @@ -538,15 +584,15 @@ var InternalClient = (function () { email: email, password: password }).then(function (res) { - _this11.client.emit("debug", "direct API login, cached token was unavailable"); + _this12.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); + _this12.tokenCacher.setToken(email, password, token); + return _this12.loginWithToken(token, email, password); }, function (error) { - _this11.websocket = null; + _this12.websocket = null; throw error; })["catch"](function (error) { - _this11.state = _ConnectionState2["default"].DISCONNECTED; + _this12.state = _ConnectionState2["default"].DISCONNECTED; client.emit("disconnected"); throw error; }); @@ -555,28 +601,28 @@ var InternalClient = (function () { // def logout InternalClient.prototype.logout = function logout() { - var _this12 = this; + var _this13 = this; if (this.state === _ConnectionState2["default"].DISCONNECTED || this.state === _ConnectionState2["default"].IDLE) { return Promise.reject(new Error("Client is not logged in!")); } return this.apiRequest("post", _Constants.Endpoints.LOGOUT, true).then(function () { - if (_this12.websocket) { - _this12.websocket.close(); - _this12.websocket = null; + if (_this13.websocket) { + _this13.websocket.close(); + _this13.websocket = null; } - _this12.token = null; - _this12.email = null; - _this12.password = null; - _this12.state = _ConnectionState2["default"].DISCONNECTED; + _this13.token = null; + _this13.email = null; + _this13.password = null; + _this13.state = _ConnectionState2["default"].DISCONNECTED; }); }; // def startPM InternalClient.prototype.startPM = function startPM(resUser) { - var _this13 = this; + var _this14 = this; var user = this.resolver.resolveUser(resUser); if (!user) { @@ -586,7 +632,7 @@ var InternalClient = (function () { return this.apiRequest("post", _Constants.Endpoints.USER_CHANNELS(this.user.id), true, { recipient_id: user.id }).then(function (res) { - return _this13.private_channels.add(new _StructuresPMChannel2["default"](res, _this13.client)); + return _this14.private_channels.add(new _StructuresPMChannel2["default"](res, _this14.client)); }); }; @@ -601,19 +647,19 @@ var InternalClient = (function () { // def sendMessage InternalClient.prototype.sendMessage = function sendMessage(where, _content) { - var _this14 = this; + var _this15 = this; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; return this.resolver.resolveChannel(where).then(function (destination) { //var destination; - var content = _this14.resolver.resolveString(_content); + var content = _this15.resolver.resolveString(_content); - return _this14.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(destination.id), true, { + return _this15.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, _this14.client)); + return destination.messages.add(new _StructuresMessage2["default"](res, destination, _this15.client)); }); }); }; @@ -621,7 +667,7 @@ var InternalClient = (function () { // def deleteMessage InternalClient.prototype.deleteMessage = function deleteMessage(_message) { - var _this15 = this; + var _this16 = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; @@ -632,7 +678,7 @@ var InternalClient = (function () { var chain = options.wait ? delay(options.wait) : Promise.resolve(); return chain.then(function () { - return _this15.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); }); @@ -641,7 +687,7 @@ var InternalClient = (function () { // def updateMessage InternalClient.prototype.updateMessage = function updateMessage(msg, _content) { - var _this16 = this; + var _this17 = this; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; @@ -657,14 +703,14 @@ var InternalClient = (function () { content: content, tts: options.tts }).then(function (res) { - return message.channel.messages.update(message, new _StructuresMessage2["default"](res, message.channel, _this16.client)); + return message.channel.messages.update(message, new _StructuresMessage2["default"](res, message.channel, _this17.client)); }); }; // def sendFile InternalClient.prototype.sendFile = function sendFile(where, _file, name) { - var _this17 = this; + var _this18 = this; if (!name) { if (_file instanceof String || typeof _file === "string") { @@ -678,12 +724,12 @@ var InternalClient = (function () { } return this.resolver.resolveChannel(where).then(function (channel) { - return _this17.resolver.resolveFile(_file).then(function (file) { - return _this17.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id), true, null, { + return _this18.resolver.resolveFile(_file).then(function (file) { + return _this18.apiRequest("post", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id), true, null, { name: name, file: file }).then(function (res) { - return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this17.client)); + return channel.messages.add(new _StructuresMessage2["default"](res, channel, _this18.client)); }); }); }); @@ -692,7 +738,7 @@ var InternalClient = (function () { // def getChannelLogs InternalClient.prototype.getChannelLogs = function getChannelLogs(_channel) { - var _this18 = this; + var _this19 = this; var limit = arguments.length <= 1 || arguments[1] === undefined ? 50 : arguments[1]; var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; @@ -700,21 +746,21 @@ var InternalClient = (function () { return this.resolver.resolveChannel(_channel).then(function (channel) { var qsObject = { limit: limit }; if (options.before) { - var res = _this18.resolver.resolveMessage(options.before); + var res = _this19.resolver.resolveMessage(options.before); if (res) { qsObject.before = res.id; } } if (options.after) { - var res = _this18.resolver.resolveMessage(options.after); + var res = _this19.resolver.resolveMessage(options.after); if (res) { qsObject.after = res.id; } } - return _this18.apiRequest("get", _Constants.Endpoints.CHANNEL_MESSAGES(channel.id) + "?" + _querystring2["default"].stringify(qsObject), true).then(function (res) { + return _this19.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, _this18.client)); + return channel.messages.add(new _StructuresMessage2["default"](msg, channel, _this19.client)); }); }); }); @@ -723,13 +769,13 @@ var InternalClient = (function () { // def getBans InternalClient.prototype.getBans = function getBans(server) { - var _this19 = this; + var _this20 = 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 _this19.users.add(new _StructuresUser2["default"](ban.user, _this19.client)); + return _this20.users.add(new _StructuresUser2["default"](ban.user, _this20.client)); }); }); }; @@ -737,7 +783,7 @@ var InternalClient = (function () { // def createChannel InternalClient.prototype.createChannel = function createChannel(server, name) { - var _this20 = this; + var _this21 = this; var type = arguments.length <= 2 || arguments[2] === undefined ? "text" : arguments[2]; @@ -749,23 +795,23 @@ var InternalClient = (function () { }).then(function (res) { var channel; if (res.type === "text") { - channel = new _StructuresTextChannel2["default"](res, _this20.client, server); + channel = new _StructuresTextChannel2["default"](res, _this21.client, server); } else { - channel = new _StructuresVoiceChannel2["default"](res, _this20.client, server); + channel = new _StructuresVoiceChannel2["default"](res, _this21.client, server); } - return server.channels.add(_this20.channels.add(channel)); + return server.channels.add(_this21.channels.add(channel)); }); }; // def deleteChannel InternalClient.prototype.deleteChannel = function deleteChannel(_channel) { - var _this21 = this; + var _this22 = this; return this.resolver.resolveChannel(_channel).then(function (channel) { - return _this21.apiRequest("del", _Constants.Endpoints.CHANNEL(channel.id), true).then(function () { + return _this22.apiRequest("del", _Constants.Endpoints.CHANNEL(channel.id), true).then(function () { channel.server.channels.remove(channel); - _this21.channels.remove(channel); + _this22.channels.remove(channel); }); }); }; @@ -803,7 +849,7 @@ var InternalClient = (function () { // def moveMember InternalClient.prototype.moveMember = function moveMember(user, channel) { - var _this22 = this; + var _this23 = this; user = this.resolver.resolveUser(user); return this.resolver.resolveChannel(channel).then(function (channel) { @@ -813,14 +859,14 @@ var InternalClient = (function () { if (channel.type !== "voice") { throw new Error("Can't moveMember into a non-voice channel"); } else { - return _this22.apiRequest("patch", _Constants.Endpoints.SERVER_MEMBERS(server.id) + "/" + user.id, true, { channel_id: channel.id }).then(function (res) { + return _this23.apiRequest("patch", _Constants.Endpoints.SERVER_MEMBERS(server.id) + "/" + user.id, true, { channel_id: channel.id }).then(function (res) { user.voiceChannel = channel; return res; }); } }); }; - + // def muteMember InternalClient.prototype.muteMember = function muteMember(user, server) { @@ -856,15 +902,15 @@ var InternalClient = (function () { // def createRole InternalClient.prototype.createRole = function createRole(server, data) { - var _this23 = this; + var _this24 = 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, _this23.client)); + var role = server.roles.add(new _StructuresRole2["default"](res, server, _this24.client)); if (data) { - return _this23.updateRole(role, data); + return _this24.updateRole(role, data); } return role; }); @@ -873,7 +919,7 @@ var InternalClient = (function () { // def updateRole InternalClient.prototype.updateRole = function updateRole(role, data) { - var _this24 = this; + var _this25 = this; role = this.resolver.resolveRole(role); var server = this.resolver.resolveServer(role.server); @@ -910,7 +956,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, _this24.client)); + return server.roles.update(role, new _StructuresRole2["default"](res, server, _this25.client)); }); }; @@ -927,7 +973,7 @@ var InternalClient = (function () { //def addMemberToRole InternalClient.prototype.addMemberToRole = function addMemberToRole(member, roles) { - var _this25 = this; + var _this26 = this; member = this.resolver.resolveUser(member); @@ -944,7 +990,7 @@ var InternalClient = (function () { } } else { roles = roles.map(function (r) { - return _this25.resolver.resolveRole(r); + return _this26.resolver.resolveRole(r); }); } @@ -1007,7 +1053,7 @@ var InternalClient = (function () { //def removeMemberFromRole InternalClient.prototype.removeMemberFromRole = function removeMemberFromRole(member, roles) { - var _this26 = this; + var _this27 = this; member = this.resolver.resolveUser(member); @@ -1024,7 +1070,7 @@ var InternalClient = (function () { } } else { roles = roles.map(function (r) { - return _this26.resolver.resolveRole(r); + return _this27.resolver.resolveRole(r); }); } @@ -1065,7 +1111,7 @@ var InternalClient = (function () { // def createInvite InternalClient.prototype.createInvite = function createInvite(chanServ, options) { - var _this27 = this; + var _this28 = this; return this.resolver.resolveChannel(chanServ).then(function (channel) { if (!options) { @@ -1079,8 +1125,8 @@ var InternalClient = (function () { options.xkcdpass = options.xkcd || false; } - return _this27.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true, options).then(function (res) { - return new _StructuresInvite2["default"](res, _this27.channels.get("id", res.channel.id), _this27.client); + return _this28.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true, options).then(function (res) { + return new _StructuresInvite2["default"](res, _this28.channels.get("id", res.channel.id), _this28.client); }); }); }; @@ -1098,7 +1144,7 @@ var InternalClient = (function () { //def getInvite InternalClient.prototype.getInvite = function getInvite(invite) { - var _this28 = this; + var _this29 = this; invite = this.resolver.resolveInviteID(invite); if (!invite) { @@ -1106,11 +1152,11 @@ var InternalClient = (function () { } return this.apiRequest("get", _Constants.Endpoints.INVITE(invite), true).then(function (res) { - if (!_this28.channels.has("id", res.channel.id)) { - return new _StructuresInvite2["default"](res, null, _this28.client); + if (!_this29.channels.has("id", res.channel.id)) { + return new _StructuresInvite2["default"](res, null, _this29.client); } - return _this28.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(res.channel.id), true, { validate: invite }).then(function (res2) { - return new _StructuresInvite2["default"](res2, _this28.channels.get("id", res.channel.id), _this28.client); + return _this29.apiRequest("post", _Constants.Endpoints.CHANNEL_INVITES(res.channel.id), true, { validate: invite }).then(function (res2) { + return new _StructuresInvite2["default"](res2, _this29.channels.get("id", res.channel.id), _this29.client); }); }); }; @@ -1118,22 +1164,22 @@ var InternalClient = (function () { //def getInvites InternalClient.prototype.getInvites = function getInvites(channel) { - var _this29 = this; + var _this30 = 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, _this29.channels.get("id", data.channel.id), _this29.client); + return new _StructuresInvite2["default"](data, _this30.channels.get("id", data.channel.id), _this30.client); }); }); } } return this.resolver.resolveChannel(channel).then(function (channel) { - return _this29.apiRequest("get", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true).then(function (res) { + return _this30.apiRequest("get", _Constants.Endpoints.CHANNEL_INVITES(channel.id), true).then(function (res) { return res.map(function (data) { - return new _StructuresInvite2["default"](data, _this29.channels.get("id", data.channel.id), _this29.client); + return new _StructuresInvite2["default"](data, _this30.channels.get("id", data.channel.id), _this30.client); }); }); }); @@ -1142,7 +1188,7 @@ var InternalClient = (function () { //def overwritePermissions InternalClient.prototype.overwritePermissions = function overwritePermissions(channel, role, updated) { - var _this30 = this; + var _this31 = this; return this.resolver.resolveChannel(channel).then(function (channel) { if (!channel instanceof _StructuresServerChannel2["default"]) { @@ -1155,7 +1201,7 @@ var InternalClient = (function () { }; if (role instanceof String || typeof role === "string") { - role = _this30.resolver.resolveUser(role) || _this30.resolver.resolveRole(role); + role = _this31.resolver.resolveUser(role) || _this31.resolver.resolveRole(role); } if (role instanceof _StructuresUser2["default"]) { @@ -1188,7 +1234,7 @@ var InternalClient = (function () { } } - return _this30.apiRequest("put", _Constants.Endpoints.CHANNEL_PERMISSIONS(channel.id) + "/" + data.id, true, data); + return _this31.apiRequest("put", _Constants.Endpoints.CHANNEL_PERMISSIONS(channel.id) + "/" + data.id, true, data); }); }; @@ -1227,49 +1273,49 @@ var InternalClient = (function () { //def sendTyping InternalClient.prototype.sendTyping = function sendTyping(channel) { - var _this31 = this; + var _this32 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - return _this31.apiRequest("post", _Constants.Endpoints.CHANNEL(channel.id) + "/typing", true); + return _this32.apiRequest("post", _Constants.Endpoints.CHANNEL(channel.id) + "/typing", true); }); }; //def startTyping InternalClient.prototype.startTyping = function startTyping(channel) { - var _this32 = this; + var _this33 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - if (_this32.intervals.typing[channel.id]) { + if (_this33.intervals.typing[channel.id]) { // typing interval already exists, leave it alone throw new Error("Already typing in that channel"); } - _this32.intervals.typing[channel.id] = setInterval(function () { - return _this32.sendTyping(channel)["catch"](function (error) { - return _this32.emit("error", error); + _this33.intervals.typing[channel.id] = setInterval(function () { + return _this33.sendTyping(channel)["catch"](function (error) { + return _this33.emit("error", error); }); }, 4000); - return _this32.sendTyping(channel); + return _this33.sendTyping(channel); }); }; //def stopTyping InternalClient.prototype.stopTyping = function stopTyping(channel) { - var _this33 = this; + var _this34 = this; return this.resolver.resolveChannel(channel).then(function (channel) { - if (!_this33.intervals.typing[channel.id]) { + if (!_this34.intervals.typing[channel.id]) { // typing interval doesn"t exist throw new Error("Not typing in that channel"); } - clearInterval(_this33.intervals.typing[channel.id]); - _this33.intervals.typing[channel.id] = false; + clearInterval(_this34.intervals.typing[channel.id]); + _this34.intervals.typing[channel.id] = false; }); }; @@ -1307,12 +1353,12 @@ var InternalClient = (function () { //def setChannelTopic InternalClient.prototype.setChannelTopic = function setChannelTopic(chann) { - var _this34 = this; + var _this35 = this; var topic = arguments.length <= 1 || arguments[1] === undefined ? "" : arguments[1]; return this.resolver.resolveChannel(chann).then(function (channel) { - return _this34.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { + return _this35.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { name: channel.name, position: channel.position, topic: topic @@ -1325,12 +1371,12 @@ var InternalClient = (function () { //def setChannelName InternalClient.prototype.setChannelName = function setChannelName(chann) { - var _this35 = this; + var _this36 = this; var name = arguments.length <= 1 || arguments[1] === undefined ? "discordjs_is_the_best" : arguments[1]; return this.resolver.resolveChannel(chann).then(function (channel) { - return _this35.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { + return _this36.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { name: name, position: channel.position, topic: channel.topic @@ -1343,13 +1389,13 @@ var InternalClient = (function () { //def setChannelNameAndTopic InternalClient.prototype.setChannelNameAndTopic = function setChannelNameAndTopic(chann) { - var _this36 = this; + var _this37 = this; var name = arguments.length <= 1 || arguments[1] === undefined ? "discordjs_is_the_best" : arguments[1]; var topic = arguments.length <= 2 || arguments[2] === undefined ? "" : arguments[2]; return this.resolver.resolveChannel(chann).then(function (channel) { - return _this36.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { + return _this37.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { name: name, position: channel.position, topic: topic @@ -1363,12 +1409,12 @@ var InternalClient = (function () { //def setTopic InternalClient.prototype.setChannelPosition = function setChannelPosition(chann) { - var _this37 = this; + var _this38 = this; var position = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; return this.resolver.resolveChannel(chann).then(function (channel) { - return _this37.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { + return _this38.apiRequest("patch", _Constants.Endpoints.CHANNEL(channel.id), true, { name: channel.name, position: position, topic: channel.topic @@ -1430,7 +1476,7 @@ var InternalClient = (function () { }; InternalClient.prototype.createWS = function createWS(url) { - var _this38 = this; + var _this39 = this; var self = this; var client = self.client; @@ -1938,7 +1984,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; + _this39.email = data.email || _this39.email; var presenceUser = new _StructuresUser2["default"](data, client); diff --git a/src/Client/Client.js b/src/Client/Client.js index 9142c7ed0..3bdc4d67d 100644 --- a/src/Client/Client.js +++ b/src/Client/Client.js @@ -158,11 +158,11 @@ export default class Client extends EventEmitter { } /** - * The active voice connection of the Client, or null if not applicable. Only available after `ready` event has been emitted. - * @type {VoiceConnection|null} the voice connection (if any). + * A cache of active voice connection of the Client, or null if not applicable. Only available after `ready` event has been emitted. + * @type {Cache} a Cache of Voice Connections */ - get voiceConnection() { - return this.internal.voiceConnection; + get voiceConnections() { + return this.internal.voiceConnections; } /** @@ -762,7 +762,7 @@ export default class Client extends EventEmitter { return this.internal.moveMember(user, channel) .then(dataCallback(callback), errorCallback(callback)); } - + // def muteMember muteMember(user, server, callback = (/*err, {}*/) => { }) { return this.internal.muteMember(user, server) @@ -973,8 +973,8 @@ export default class Client extends EventEmitter { } // def leaveVoiceChannel - leaveVoiceChannel(callback = (/*err, {}*/) => { }) { - return this.internal.leaveVoiceChannel() + leaveVoiceChannel(chann, callback = (/*err, {}*/) => { }) { + return this.internal.leaveVoiceChannel(chann) .then(dataCallback(callback), errorCallback(callback)); } diff --git a/src/Client/InternalClient.js b/src/Client/InternalClient.js index 05e36c2bb..a8f84023f 100644 --- a/src/Client/InternalClient.js +++ b/src/Client/InternalClient.js @@ -122,7 +122,7 @@ export default class InternalClient { misc : [] }; - this.voiceConnection = null; + this.voiceConnections = new Cache(); this.resolver = new Resolver(this); this.readyTime = null; this.messageAwaits = {}; @@ -173,12 +173,45 @@ export default class InternalClient { } //def leaveVoiceChannel - leaveVoiceChannel() { - if (this.voiceConnection) { - this.voiceConnection.destroy(); - this.voiceConnection = null; + leaveVoiceChannel(chann) { + if (this.user.bot) { + var leave = (connection) => { + return new Promise((resolve, reject) => { + connection.destroy(); + this.voiceConnections.remove(connection); + resolve(); + }); + }; + + if (chann instanceof VoiceChannel) { + return this.resolver.resolveChannel(chann).then(channel => { + if (!channel) { + return Promise.reject(new Error("voice channel does not exist")); + } + + if (channel.type !== 'voice') { + return Promise.reject(new Error("channel is not a voice channel!")); + } + + var connection = this.voiceConnections.get("voiceChannel", channel); + if (!connection) { + return Promise.reject(new Error("not connected to that voice channel")); + } + return leave(connection); + }); + } else if (chann instanceof VoiceConnection) { + return leave(chann); + } else { + return Promise.reject(new Error("invalid voice channel/connection to leave")) + } + } else { + // preserve old functionality for non-bots + if (this.voiceConnections[0]) { + this.voiceConnections[0].destroy(); + this.voiceConnections = []; + } + return Promise.resolve(); } - return Promise.resolve(); } //def awaitResponse @@ -213,43 +246,52 @@ export default class InternalClient { return Promise.reject(new Error("channel is not a voice channel!")); } - return this.leaveVoiceChannel() - .then(() => { - return new Promise((resolve, reject) => { - var session, token, server = channel.server, endpoint; + var joinVoice = new Promise((resolve, reject) => { + var session, token, server = channel.server, endpoint; - var check = m => { - var data = JSON.parse(m); - if (data.t === "VOICE_STATE_UPDATE") { - session = data.d.session_id; - } else if (data.t === "VOICE_SERVER_UPDATE") { - token = data.d.token; - endpoint = data.d.endpoint; - var chan = this.voiceConnection = new VoiceConnection( - channel, this.client, session, token, server, endpoint - ); - - chan.on("ready", () => resolve(chan)); - chan.on("error", reject); - - this.client.emit("debug", "removed temporary voice websocket listeners"); - this.websocket.removeListener("message", check); + var check = m => { + var data = JSON.parse(m); + if (data.d.guild_id !== server.id) return // ensure it is the right server + if (data.t === "VOICE_STATE_UPDATE") { + session = data.d.session_id; + if (existingServerConn && data.d.channel_id !== existingServerConn.voiceChannel.id) { + existingServerConn.voiceChannel = channel; // existing connection to that server, just channel changed. it is the same connection, just a different channel } - }; + } else if (data.t === "VOICE_SERVER_UPDATE") { + token = data.d.token; + endpoint = data.d.endpoint; + var chan = new VoiceConnection( + channel, this.client, session, token, server, endpoint + ); + this.voiceConnections.add(chan); - this.websocket.on("message", check); - this.sendWS({ - op: 4, - d: { - "guild_id": server.id, - "channel_id": channel.id, - "self_mute": false, - "self_deaf": false - } - }); + chan.on("ready", () => resolve(chan)); + chan.on("error", reject); + + this.client.emit("debug", "removed temporary voice websocket listeners"); + this.websocket.removeListener("message", check); + } + }; + + this.websocket.on("message", check); + this.sendWS({ + op: 4, + d: { + "guild_id": server.id, + "channel_id": channel.id, + "self_mute": false, + "self_deaf": false + } }); }); + + if (!this.user.bot && this.voiceConnections.length > 0) // nonbot, one voiceconn only, just like last time just disconnect + return this.leaveVoiceChannel(this.voiceConnections[0]).then(joinVoice); + + var existingServerConn = this.voiceConnections.get("server", channel.server); // same server connection + + return joinVoice; }); } @@ -654,28 +696,28 @@ export default class InternalClient { } }); } - + // def muteMember muteMember(user, server) { user = this.resolver.resolveUser(user); server = this.resolver.resolveServer(server); return this.apiRequest("patch", `${Endpoints.SERVER_MEMBERS(server.id)}/${user.id}`, true, { mute: true }); } - + // def unmuteMember unmuteMember(user, server) { user = this.resolver.resolveUser(user); server = this.resolver.resolveServer(server); return this.apiRequest("patch", `${Endpoints.SERVER_MEMBERS(server.id)}/${user.id}`, true, { mute: false }); } - + // def deafenMember deafenMember(user, server) { user = this.resolver.resolveUser(user); server = this.resolver.resolveServer(server); return this.apiRequest("patch", `${Endpoints.SERVER_MEMBERS(server.id)}/${user.id}`, true, { deaf: true }); } - + // def undeafenMember undeafenMember(user, server) { user = this.resolver.resolveUser(user);