Files
discord.js/src/client/voice/ClientVoiceManager.js
Schuyler Cebulskie 0b908f5bce Cleanup Part 2: Electric Boogaloo (Reloaded) (#594)
* Cleanup Part 2: Electric Boogaloo (Reloaded)

* Moar cleanup

* Tweak NOT_A_PERMISSION error
2016-09-04 10:08:09 +01:00

125 lines
4.1 KiB
JavaScript

const Collection = require('../../util/Collection');
const mergeDefault = require('../../util/MergeDefault');
const Constants = require('../../util/Constants');
const VoiceConnection = require('./VoiceConnection');
/**
* Manages all the voice stuff for the Client
* @private
*/
class ClientVoiceManager {
constructor(client) {
/**
* The client that instantiated this voice manager
* @type {Client}
*/
this.client = client;
/**
* A collection mapping connection IDs to the Connection objects
* @type {Collection<string, VoiceConnection>}
*/
this.connections = new Collection();
/**
* Pending connection attempts, maps Guild ID to VoiceChannel
* @type {Collection<string, VoiceChannel>}
*/
this.pending = new Collection();
}
/**
* Checks whether a pending request can be processed
* @private
* @param {string} guildID The ID of the Guild
*/
_checkPendingReady(guildID) {
const pendingRequest = this.pending.get(guildID);
if (!pendingRequest) throw new Error('Guild not pending');
if (pendingRequest.token && pendingRequest.sessionID && pendingRequest.endpoint) {
const { channel, token, sessionID, endpoint, resolve, reject } = pendingRequest;
const voiceConnection = new VoiceConnection(this, channel, token, sessionID, endpoint, resolve, reject);
this.pending.delete(guildID);
this.connections.set(guildID, voiceConnection);
voiceConnection.once('disconnected', () => {
this.connections.delete(guildID);
});
}
}
/**
* Called when the Client receives information about this voice server update.
* @param {string} guildID The ID of the Guild
* @param {string} token The token to authorise with
* @param {string} endpoint The endpoint to connect to
*/
_receivedVoiceServer(guildID, token, endpoint) {
const pendingRequest = this.pending.get(guildID);
if (!pendingRequest) throw new Error('Guild not pending');
pendingRequest.token = token;
// remove the port otherwise it errors ¯\_(ツ)_/¯
pendingRequest.endpoint = endpoint.match(/([^:]*)/)[0];
this._checkPendingReady(guildID);
}
/**
* Called when the Client receives information about the voice state update.
* @param {string} guildID The ID of the Guild
* @param {string} sessionID The session id to authorise with
*/
_receivedVoiceStateUpdate(guildID, sessionID) {
const pendingRequest = this.pending.get(guildID);
if (!pendingRequest) throw new Error('Guild not pending');
pendingRequest.sessionID = sessionID;
this._checkPendingReady(guildID);
}
/**
* Sends a request to the main gateway to join a voice channel
* @param {VoiceChannel} channel The channel to join
* @param {Object} [options] The options to provide
*/
_sendWSJoin(channel, options = {}) {
options = mergeDefault({
guild_id: channel.guild.id,
channel_id: channel.id,
self_mute: false,
self_deaf: false,
}, options);
this.client.ws.send({
op: Constants.OPCodes.VOICE_STATE_UPDATE,
d: options,
});
}
/**
* Sets up a request to join a voice channel
* @param {VoiceChannel} channel The voice channel to join
* @returns {Promise<VoiceConnection>}
*/
joinChannel(channel) {
return new Promise((resolve, reject) => {
if (this.pending.get(channel.guild.id)) throw new Error('already connecting to a channel in this guild');
const existingConn = this.connections.get(channel.guild.id);
if (existingConn) {
if (existingConn.channel.id !== channel.id) {
this._sendWSJoin(channel);
this.connections.get(channel.guild.id).channel = channel;
}
resolve(existingConn);
return;
}
this.pending.set(channel.guild.id, {
channel,
sessionID: null,
token: null,
endpoint: null,
resolve,
reject,
});
this._sendWSJoin(channel);
this.client.setTimeout(() => reject(new Error('connection not established in 15s time period')), 15000);
});
}
}
module.exports = ClientVoiceManager;