Cleanup Part 2: Electric Boogaloo (Reloaded) (#594)

* Cleanup Part 2: Electric Boogaloo (Reloaded)

* Moar cleanup

* Tweak NOT_A_PERMISSION error
This commit is contained in:
Schuyler Cebulskie
2016-09-04 05:08:09 -04:00
committed by Amish Shah
parent 5a9c42061f
commit 0b908f5bce
95 changed files with 946 additions and 1526 deletions

View File

@@ -15,10 +15,8 @@ const Collection = require('../util/Collection');
* @extends {EventEmitter}
*/
class Client extends EventEmitter {
/**
* Creates an instance of Client.
* @param {ClientOptions} [options] options to pass to the client
* @param {ClientOptions} [options] Options for the client
*/
constructor(options) {
super();
@@ -129,17 +127,13 @@ class Client extends EventEmitter {
* client.login(email, password);
*/
login(emailOrToken, password) {
if (password) {
// login with email and password
return this.rest.methods.loginEmailPassword(emailOrToken, password);
}
// login with token
if (password) return this.rest.methods.loginEmailPassword(emailOrToken, password);
return this.rest.methods.loginToken(emailOrToken);
}
/**
* Destroys the client and logs out. Resolves with null if successful.
* @returns {Promise<null, Error>}
* Destroys the client and logs out.
* @returns {Promise}
*/
destroy() {
return new Promise((resolve, reject) => {
@@ -152,8 +146,7 @@ class Client extends EventEmitter {
this._timeouts = [];
this._intervals = [];
resolve();
})
.catch(reject);
}).catch(reject);
});
}
@@ -176,7 +169,7 @@ class Client extends EventEmitter {
/**
* This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
* if you wish to force a sync of Guild data, you can use this. Only applicable to user accounts.
* @param {array<Guild>} [guilds=this.guilds.array()] An array of guilds to sync.
* @param {Guild[]} [guilds=this.guilds.array()] An array of guilds to sync
*/
syncGuilds(guilds = this.guilds.array()) {
if (!this.user.bot) {
@@ -215,7 +208,6 @@ class Client extends EventEmitter {
get uptime() {
return this.readyTime ? Date.now() - this.readyTime : null;
}
}
module.exports = Client;

View File

@@ -23,11 +23,10 @@ class ClientDataManager {
this.client.guilds.set(guild.id, guild);
if (this.pastReady && !already) {
/**
* Emitted whenever the client joins a Guild.
*
* @event Client#guildCreate
* @param {Guild} guild the created guild.
*/
* Emitted whenever the client joins a Guild.
* @event Client#guildCreate
* @param {Guild} guild The created guild
*/
this.client.emit(Constants.Events.GUILD_CREATE, guild);
}
@@ -35,16 +34,13 @@ class ClientDataManager {
}
newUser(data) {
if (this.client.users.has(data.id)) {
return this.client.users.get(data.id);
}
if (this.client.users.has(data.id)) return this.client.users.get(data.id);
const user = new User(this.client, data);
this.client.users.set(user.id, user);
return user;
}
newChannel(data, $guild) {
let guild = $guild;
newChannel(data, guild) {
const already = this.client.channels.has(data.id);
let channel;
if (data.type === Constants.ChannelTypes.DM) {
@@ -65,22 +61,18 @@ class ClientDataManager {
}
if (channel) {
if (this.pastReady && !already) {
this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
}
if (this.pastReady && !already) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
this.client.channels.set(channel.id, channel);
return channel;
}
return null;
}
killGuild(guild) {
const already = this.client.guilds.has(guild.id);
this.client.guilds.delete(guild.id);
if (already && this.pastReady) {
this.client.emit(Constants.Events.GUILD_DELETE, guild);
}
if (already && this.pastReady) this.client.emit(Constants.Events.GUILD_DELETE, guild);
}
killUser(user) {
@@ -89,17 +81,13 @@ class ClientDataManager {
killChannel(channel) {
this.client.channels.delete(channel.id);
if (channel instanceof GuildChannel) {
channel.guild.channels.delete(channel.id);
}
if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id);
}
updateGuild(currentGuild, newData) {
const oldGuild = cloneObject(currentGuild);
currentGuild.setup(newData);
if (this.pastReady) {
this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
}
if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
}
updateChannel(currentChannel, newData) {

View File

@@ -2,13 +2,12 @@ const path = require('path');
const fs = require('fs');
const request = require('superagent');
const getStructure = name => require(`../structures/${name}`);
const User = getStructure('User');
const Message = getStructure('Message');
const Guild = getStructure('Guild');
const Channel = getStructure('Channel');
const GuildMember = getStructure('GuildMember');
const Constants = require('../util/constants');
const User = require(`../structures/User`);
const Message = require(`../structures/Message`);
const Guild = require(`../structures/Guild`);
const Channel = require(`../structures/Channel`);
const GuildMember = require(`../structures/GuildMember`);
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
@@ -16,7 +15,9 @@ const GuildMember = getStructure('GuildMember');
* @private
*/
class ClientDataResolver {
/**
* @param {Client} client The client the resolver is for
*/
constructor(client) {
this.client = client;
}
@@ -33,7 +34,7 @@ class ClientDataResolver {
/**
* Resolves a UserResolvable to a User object
* @param {UserResolvable} user the UserResolvable to identify
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?User}
*/
resolveUser(user) {
@@ -60,18 +61,12 @@ class ClientDataResolver {
/**
* Resolves a GuildResolvable to a Guild object
* @param {GuildResolvable} guild the GuildResolvable to identify
* @param {GuildResolvable} guild The GuildResolvable to identify
* @returns {?Guild}
*/
resolveGuild(guild) {
if (guild instanceof Guild) {
return guild;
}
if (typeof guild === 'string') {
return this.client.guilds.get(guild);
}
if (guild instanceof Guild) return guild;
if (typeof guild === 'string') return this.client.guilds.get(guild);
return null;
}
@@ -84,21 +79,16 @@ class ClientDataResolver {
/**
* Resolves a GuildMemberResolvable to a GuildMember object
* @param {GuildResolvable} guild the guild that the member is part of
* @param {UserResolvable} user the user that is part of the guild
* @param {GuildResolvable} guild The guild that the member is part of
* @param {UserResolvable} user The user that is part of the guild
* @returns {?GuildMember}
*/
resolveGuildMember(guild, user) {
if (user instanceof GuildMember) {
return user;
}
if (user instanceof GuildMember) return user;
guild = this.resolveGuild(guild);
user = this.resolveUser(user);
if (!guild || !user) {
return null;
}
if (!guild || !user) return null;
return guild.members.get(user.id);
}
@@ -112,14 +102,11 @@ class ClientDataResolver {
/**
* Resolves a Base64Resolvable to a Base 64 image
* @param {Base64Resolvable} data the base 64 resolvable you want to resolve
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
*/
resolveBase64(data) {
if (data instanceof Buffer) {
return `data:image/jpg;base64,${data.toString('base64')}`;
}
if (data instanceof Buffer) return `data:image/jpg;base64,${data.toString('base64')}`;
return data;
}
@@ -132,43 +119,49 @@ class ClientDataResolver {
/**
* Resolves a ChannelResolvable to a Channel object
* @param {ChannelResolvable} channel the channel resolvable to resolve
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Channel}
*/
resolveChannel(channel) {
if (channel instanceof Channel) {
return channel;
}
if (typeof channel === 'string') {
return this.client.channels.get(channel.id);
}
if (channel instanceof Channel) return channel;
if (typeof channel === 'string') return this.client.channels.get(channel.id);
return null;
}
/**
* Data that can be resolved to give a permission number. This can be:
* * A string
* * A permission number
* @typedef {string|number} PermissionResolvable
*/
/**
* Resolves a PermissionResolvable to a permission number
* @param {PermissionResolvable} permission The permission resolvable to resolve
* @returns {number}
*/
resolvePermission(permission) {
if (typeof permission === 'string') permission = Constants.PermissionFlags[permission];
if (!permission) throw Constants.Errors.NOT_A_PERMISSION;
return permission;
}
/**
* Data that can be resolved to give a string. This can be:
* * A string
* * An Array (joined with a new line delimiter to give a string)
* * Any object
* @typedef {string|Array|Object} StringResolvable
* * Any value
* @typedef {string|Array|*} StringResolvable
*/
/**
* Resolves a StringResolvable to a string
* @param {StringResolvable} data the string resolvable to resolve
* @param {StringResolvable} data The string resolvable to resolve
* @returns {string}
*/
resolveString(data) {
if (typeof data === 'string') {
return data;
}
if (data instanceof Array) {
return data.join('\n');
}
if (typeof data === 'string') return data;
if (data instanceof Array) return data.join('\n');
return String(data);
}
@@ -182,8 +175,8 @@ class ClientDataResolver {
/**
* Resolves a FileResolvable to a Buffer
* @param {FileResolvable} resource the file resolvable to resolve
* @returns {string|Buffer}
* @param {FileResolvable} resource The file resolvable to resolve
* @returns {Promise<Buffer>}
*/
resolveFile(resource) {
if (typeof resource === 'string') {
@@ -194,19 +187,19 @@ class ClientDataResolver {
.end((err, res) => err ? reject(err) : resolve(res.body));
} else {
const file = path.resolve(resource);
const stat = fs.statSync(file);
if (!stat.isFile()) {
throw new Error(`The file could not be found: ${file}`);
}
fs.readFile(file, (err, data) => {
if (err) reject(err); else resolve(data);
fs.stat(file, (err, stats) => {
if (err) reject(err);
if (!stats.isFile()) throw new Error(`The file could not be found: ${file}`);
fs.readFile(file, (err2, data) => {
if (err2) reject(err2); else resolve(data);
});
});
}
});
}
return Promise.resolve(resource);
if (resource instanceof Buffer) return Promise.resolve(resource);
return Promise.reject(new TypeError('resource is not a string or Buffer'));
}
}

View File

@@ -5,7 +5,6 @@ const Constants = require('../util/Constants');
* @private
*/
class ClientManager {
constructor(client) {
/**
* The Client that instantiated this Manager
@@ -21,25 +20,22 @@ class ClientManager {
/**
* Connects the Client to the WebSocket
* @param {string} token the authorization token
* @param {function} resolve function to run when connection is successful
* @param {function} reject function to run when connection fails
* @param {string} token The authorization token
* @param {function} resolve Function to run when connection is successful
* @param {function} reject Function to run when connection fails
*/
connectToWebSocket(token, resolve, reject) {
this.client.token = token;
this.client.rest.methods.getGateway()
.then(gateway => {
this.client.ws.connect(gateway);
this.client.once(Constants.Events.READY, () => resolve(token));
})
.catch(reject);
this.client.rest.methods.getGateway().then(gateway => {
this.client.ws.connect(gateway);
this.client.once(Constants.Events.READY, () => resolve(token));
}).catch(reject);
this.client.setTimeout(() => reject(Constants.Errors.TOOK_TOO_LONG), 1000 * 300);
}
/**
* Sets up a keep-alive interval to keep the Client's connection valid
* @param {number} time the interval in milliseconds at which heartbeat packets should be sent
* @param {number} time The interval in milliseconds at which heartbeat packets should be sent
*/
setupKeepAlive(time) {
this.heartbeatInterval = this.client.setInterval(() => {

View File

@@ -11,7 +11,6 @@ that WebSocket events don't clash with REST methods.
*/
class GenericAction {
constructor(client) {
this.client = client;
}
@@ -19,7 +18,6 @@ class GenericAction {
handle(data) {
return data;
}
}
module.exports = GenericAction;

View File

@@ -1,5 +1,4 @@
class ActionsManager {
constructor(client) {
this.client = client;

View File

@@ -1,7 +1,6 @@
const Action = require('./Action');
class ChannelCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = client.dataManager.newChannel(data);

View File

@@ -1,7 +1,6 @@
const Action = require('./Action');
class ChannelDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class ChannelUpdateAction extends Action {
handle(data) {
const client = this.client;
@@ -26,11 +25,10 @@ class ChannelUpdateAction extends Action {
}
/**
* Emitted whenever a channel is updated - e.g. name change, topic change.
*
* @event Client#channelUpdate
* @param {Channel} oldChannel the channel before the update
* @param {Channel} newChannel the channel after the update
*/
* Emitted whenever a channel is updated - e.g. name change, topic change.
* @event Client#channelUpdate
* @param {Channel} oldChannel The channel before the update
* @param {Channel} newChannel The channel after the update
*/
module.exports = ChannelUpdateAction;

View File

@@ -2,7 +2,6 @@ const Action = require('./Action');
const Constants = require('../../util/Constants');
class GuildBanRemove extends Action {
handle(data) {
const client = this.client;
const guild = client.guilds.get(data.guild_id);

View File

@@ -2,7 +2,6 @@ const Action = require('./Action');
const Constants = require('../../util/Constants');
class GuildDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
@@ -45,9 +44,8 @@ class GuildDeleteAction extends Action {
/**
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
*
* @event Client#guildUnavailable
* @param {Guild} guild the guild that has become unavailable.
*/
* @param {Guild} guild The guild that has become unavailable.
*/
module.exports = GuildDeleteAction;

View File

@@ -2,7 +2,6 @@ const Action = require('./Action');
const Constants = require('../../util/Constants');
class GuildMemberRemoveAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
@@ -43,10 +42,9 @@ class GuildMemberRemoveAction extends Action {
/**
* Emitted whenever a member leaves a guild, or is kicked.
*
* @event Client#guildMemberRemove
* @param {Guild} guild the guild that the member has left.
* @param {GuildMember} member the member that has left the guild.
* @param {Guild} guild The guild that the member has left.
* @param {GuildMember} member The member that has left the guild.
*/
module.exports = GuildMemberRemoveAction;

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const Role = require('../../structures/Role');
class GuildRoleCreate extends Action {
handle(data) {
const client = this.client;
@@ -25,11 +24,10 @@ class GuildRoleCreate extends Action {
}
/**
* Emitted whenever a guild role is created.
*
* @event Client#guildRoleCreate
* @param {Guild} guild the guild that the role was created in.
* @param {Role} role the role that was created.
*/
* Emitted whenever a guild role is created.
* @event Client#guildRoleCreate
* @param {Guild} guild The guild that the role was created in.
* @param {Role} role The role that was created.
*/
module.exports = GuildRoleCreate;

View File

@@ -2,7 +2,6 @@ const Action = require('./Action');
const Constants = require('../../util/Constants');
class GuildRoleDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();
@@ -39,11 +38,10 @@ class GuildRoleDeleteAction extends Action {
}
/**
* Emitted whenever a guild role is deleted.
*
* @event Client#guildRoleDelete
* @param {Guild} guild the guild that the role was deleted in.
* @param {Role} role the role that was deleted.
*/
* Emitted whenever a guild role is deleted.
* @event Client#guildRoleDelete
* @param {Guild} guild The guild that the role was deleted in.
* @param {Role} role The role that was deleted.
*/
module.exports = GuildRoleDeleteAction;

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class GuildRoleUpdateAction extends Action {
handle(data) {
const client = this.client;
@@ -33,12 +32,11 @@ class GuildRoleUpdateAction extends Action {
}
/**
* Emitted whenever a guild role is updated.
*
* @event Client#guildRoleUpdated
* @param {Guild} guild the guild that the role was updated in.
* @param {Role} oldRole the role before the update.
* @param {Role} newRole the role after the update.
*/
* Emitted whenever a guild role is updated.
* @event Client#guildRoleUpdated
* @param {Guild} guild The guild that the role was updated in.
* @param {Role} oldRole The role before the update.
* @param {Role} newRole The role after the update.
*/
module.exports = GuildRoleUpdateAction;

View File

@@ -1,7 +1,6 @@
const Action = require('./Action');
class GuildSync extends Action {
handle(data) {
const client = this.client;

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class GuildUpdateAction extends Action {
handle(data) {
const client = this.client;
@@ -26,11 +25,10 @@ class GuildUpdateAction extends Action {
}
/**
* Emitted whenever a guild is updated - e.g. name change.
*
* @event Client#guildUpdate
* @param {Guild} oldGuild the guild before the update.
* @param {Guild} newGuild the guild after the update.
*/
* Emitted whenever a guild is updated - e.g. name change.
* @event Client#guildUpdate
* @param {Guild} oldGuild The guild before the update.
* @param {Guild} newGuild The guild after the update.
*/
module.exports = GuildUpdateAction;

View File

@@ -2,7 +2,6 @@ const Action = require('./Action');
const Message = require('../../structures/Message');
class MessageCreateAction extends Action {
handle(data) {
const client = this.client;

View File

@@ -1,7 +1,6 @@
const Action = require('./Action');
class MessageDeleteAction extends Action {
constructor(client) {
super(client);
this.deleted = new Map();

View File

@@ -3,7 +3,6 @@ const Collection = require('../../util/Collection');
const Constants = require('../../util/Constants');
class MessageDeleteBulkAction extends Action {
handle(data) {
const client = this.client;
const channel = client.channels.get(data.channel_id);
@@ -12,9 +11,7 @@ class MessageDeleteBulkAction extends Action {
const messages = new Collection();
for (const id of ids) {
const message = channel.messages.get(id);
if (message) {
messages.set(message.id, message);
}
if (message) messages.set(message.id, message);
}
if (messages.size > 0) client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class MessageUpdateAction extends Action {
handle(data) {
const client = this.client;
@@ -34,11 +33,10 @@ class MessageUpdateAction extends Action {
}
/**
* Emitted whenever a message is updated - e.g. embed or content change.
*
* @event Client#messageUpdate
* @param {Message} oldMessage the message before the update.
* @param {Message} newMessage the message after the update.
*/
* Emitted whenever a message is updated - e.g. embed or content change.
* @event Client#messageUpdate
* @param {Message} oldMessage The message before the update.
* @param {Message} newMessage The message after the update.
*/
module.exports = MessageUpdateAction;

View File

@@ -1,7 +1,6 @@
const Action = require('./Action');
class UserGetAction extends Action {
handle(data) {
const client = this.client;
const user = client.dataManager.newUser(data);

View File

@@ -3,7 +3,6 @@ const Constants = require('../../util/Constants');
const cloneObject = require('../../util/CloneObject');
class UserUpdateAction extends Action {
handle(data) {
const client = this.client;
@@ -32,11 +31,10 @@ class UserUpdateAction extends Action {
}
/**
* Emitted whenever a detail of the logged in User changes - e.g. username.
*
* @event Client#userUpdate
* @param {ClientUser} oldClientUser the client user before the update.
* @param {ClientUser} newClientUser the client user after the update.
*/
* Emitted whenever a detail of the logged in User changes - e.g. username.
* @event Client#userUpdate
* @param {ClientUser} oldClientUser The client user before the update.
* @param {ClientUser} newClientUser The client user after the update.
*/
module.exports = UserUpdateAction;

View File

@@ -26,17 +26,12 @@ class APIRequest {
gen() {
const apiRequest = request[this.method](this.url);
if (this.auth) {
apiRequest.set('authorization', this.getAuth());
}
if (this.auth) apiRequest.set('authorization', this.getAuth());
if (this.file && this.file.file) {
apiRequest.set('Content-Type', 'multipart/form-data');
apiRequest.attach('file', this.file.file, this.file.name);
}
if (this.data) {
apiRequest.send(this.data);
}
if (this.data) apiRequest.send(this.data);
apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent);
return apiRequest;
}

View File

@@ -5,7 +5,6 @@ const APIRequest = require('./APIRequest');
const Constants = require('../../util/Constants');
class RESTManager {
constructor(client) {
this.client = client;
this.handlers = {};

View File

@@ -1,11 +1,11 @@
const Constants = require('../../util/Constants');
const Collection = require('../../util/Collection');
const getStructure = name => require(`../../structures/${name}`);
const User = getStructure('User');
const GuildMember = getStructure('GuildMember');
const Role = getStructure('Role');
const Invite = getStructure('Invite');
const requireStructure = name => require(`../../structures/${name}`);
const User = requireStructure('User');
const GuildMember = requireStructure('GuildMember');
const Role = requireStructure('Role');
const Invite = requireStructure('Invite');
class RESTMethods {
constructor(restManager) {
@@ -45,10 +45,9 @@ class RESTMethods {
});
}
sendMessage($channel, content, tts, nonce, file) {
sendMessage(channel, content, tts, nonce, file) {
return new Promise((resolve, reject) => {
const $this = this;
let channel = $channel;
function req() {
$this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, {
@@ -101,11 +100,9 @@ class RESTMethods {
return new Promise((resolve, reject) => {
this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, {
content,
})
.then(data => {
}).then(data => {
resolve(this.rest.client.actions.MessageUpdate.handle(data).updated);
})
.catch(reject);
}).catch(reject);
});
}
@@ -114,11 +111,9 @@ class RESTMethods {
this.rest.makeRequest('post', Constants.Endpoints.guildChannels(guild.id), true, {
name: channelName,
type: channelType,
})
.then(data => {
resolve(this.rest.client.actions.ChannelCreate.handle(data).channel);
})
.catch(reject);
}).then(data => {
resolve(this.rest.client.actions.ChannelCreate.handle(data).channel);
}).catch(reject);
});
}
@@ -126,98 +121,73 @@ class RESTMethods {
const dmChannel = Array.from(this.rest.client.channels.values())
.filter(channel => channel.recipient)
.filter(channel => channel.recipient.id === recipient.id);
return dmChannel[0];
}
createDM(recipient) {
return new Promise((resolve, reject) => {
const dmChannel = this.getExistingDM(recipient);
if (dmChannel) {
return resolve(dmChannel);
}
if (dmChannel) return resolve(dmChannel);
return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.rest.client.user.id), true, {
recipient_id: recipient.id,
})
.then(data => resolve(this.rest.client.actions.ChannelCreate.handle(data).channel))
.catch(reject);
}).then(data => resolve(this.rest.client.actions.ChannelCreate.handle(data).channel)).catch(reject);
});
}
deleteChannel($channel) {
let channel = $channel;
deleteChannel(channel) {
return new Promise((resolve, reject) => {
if (channel instanceof User || channel instanceof GuildMember) {
channel = this.getExistingDM(channel);
}
this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true)
.then($data => {
const data = $data;
data.id = channel.id;
resolve(this.rest.client.actions.ChannelDelete.handle(data).channel);
})
.catch(reject);
if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel);
this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true).then(data => {
data.id = channel.id;
resolve(this.rest.client.actions.ChannelDelete.handle(data).channel);
}).catch(reject);
});
}
updateChannel(channel, $data) {
const data = $data;
updateChannel(channel, data) {
return new Promise((resolve, reject) => {
data.name = (data.name || channel.name).trim();
data.topic = data.topic || channel.topic;
data.position = data.position || channel.position;
data.bitrate = data.bitrate || channel.bitrate;
this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data)
.then(newData => {
resolve(this.rest.client.actions.ChannelUpdate.handle(newData).updated);
})
.catch(reject);
this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data).then(newData => {
resolve(this.rest.client.actions.ChannelUpdate.handle(newData).updated);
}).catch(reject);
});
}
leaveGuild(guild) {
if (guild.ownerID === this.rest.client.user.id) {
return this.deleteGuild(guild);
}
if (guild.ownerID === this.rest.client.user.id) return this.deleteGuild(guild);
return new Promise((resolve, reject) => {
this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true)
.then(() => {
resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild);
})
.catch(reject);
this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true).then(() => {
resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild);
}).catch(reject);
});
}
// untested but probably will work
deleteGuild(guild) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true)
.then(() => {
resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild);
})
.catch(reject);
this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true).then(() => {
resolve(this.rest.client.actions.GuildDelete.handle({ id: guild.id }).guild);
}).catch(reject);
});
}
getUser(userID) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('get', Constants.Endpoints.user(userID), true)
.then((data) => {
resolve(this.rest.client.actions.UserGet.handle(data).user);
})
.catch(reject);
this.rest.makeRequest('get', Constants.Endpoints.user(userID), true).then((data) => {
resolve(this.rest.client.actions.UserGet.handle(data).user);
}).catch(reject);
});
}
updateCurrentUser(_data) {
return new Promise((resolve, reject) => {
const user = this.rest.client.user;
const data = {};
const data = {};
data.username = _data.username || user.username;
data.avatar = this.rest.client.resolver.resolveBase64(_data.avatar) || user.avatar;
if (!user.bot) {
@@ -234,44 +204,15 @@ class RESTMethods {
updateGuild(guild, _data) {
return new Promise((resolve, reject) => {
/*
can contain:
name, region, verificationLevel, afkChannel, afkTimeout, icon, owner, splash
*/
const data = {};
if (_data.name) {
data.name = _data.name;
}
if (_data.region) {
data.region = _data.region;
}
if (_data.verificationLevel) {
data.verification_level = Number(_data.verificationLevel);
}
if (_data.afkChannel) {
data.afk_channel_id = this.rest.client.resolver.resolveChannel(_data.afkChannel).id;
}
if (_data.afkTimeout) {
data.afk_timeout = Number(_data.afkTimeout);
}
if (_data.icon) {
data.icon = this.rest.client.resolver.resolveBase64(_data.icon);
}
if (_data.owner) {
data.owner_id = this.rest.client.resolver.resolveUser(_data.owner).id;
}
if (_data.splash) {
data.splash = this.rest.client.resolver.resolveBase64(_data.splash);
}
if (_data.name) data.name = _data.name;
if (_data.region) data.region = _data.region;
if (_data.verificationLevel) data.verification_level = Number(_data.verificationLevel);
if (_data.afkChannel) data.afk_channel_id = this.rest.client.resolver.resolveChannel(_data.afkChannel).id;
if (_data.afkTimeout) data.afk_timeout = Number(_data.afkTimeout);
if (_data.icon) data.icon = this.rest.client.resolver.resolveBase64(_data.icon);
if (_data.owner) data.owner_id = this.rest.client.resolver.resolveUser(_data.owner).id;
if (_data.splash) data.splash = this.rest.client.resolver.resolveBase64(_data.splash);
this.rest.makeRequest('patch', Constants.Endpoints.guild(guild.id), true, data)
.then(newData => resolve(this.rest.client.actions.GuildUpdate.handle(newData).updated))
@@ -281,40 +222,34 @@ class RESTMethods {
kickGuildMember(guild, member) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true)
.then(() => {
resolve(this.rest.client.actions.GuildMemberRemove.handle({
guild_id: guild.id,
user: member.user,
}).member);
})
.catch(reject);
this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true).then(() => {
resolve(this.rest.client.actions.GuildMemberRemove.handle({
guild_id: guild.id,
user: member.user,
}).member);
}).catch(reject);
});
}
createGuildRole(guild) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true)
.then(role => {
resolve(this.rest.client.actions.GuildRoleCreate.handle({
guild_id: guild.id,
role,
}).role);
})
.catch(reject);
this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true).then(role => {
resolve(this.rest.client.actions.GuildRoleCreate.handle({
guild_id: guild.id,
role,
}).role);
}).catch(reject);
});
}
deleteGuildRole(role) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true)
.then(() => {
resolve(this.rest.client.actions.GuildRoleDelete.handle({
guild_id: role.guild.id,
role_id: role.id,
}).role);
})
.catch(reject);
this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true).then(() => {
resolve(this.rest.client.actions.GuildRoleDelete.handle({
guild_id: role.guild.id,
role_id: role.id,
}).role);
}).catch(reject);
});
}
@@ -338,23 +273,14 @@ class RESTMethods {
getChannelMessages(channel, payload = {}) {
return new Promise((resolve, reject) => {
const params = [];
if (payload.limit) {
params.push(`limit=${payload.limit}`);
}
if (payload.around) {
params.push(`around=${payload.around}`);
} else if (payload.before) {
params.push(`before=${payload.before}`);
} else if (payload.after) {
params.push(`after=${payload.after}`);
}
if (payload.limit) params.push(`limit=${payload.limit}`);
if (payload.around) params.push(`around=${payload.around}`);
else if (payload.before) params.push(`before=${payload.before}`);
else if (payload.after) params.push(`after=${payload.after}`);
let request = Constants.Endpoints.channelMessages(channel.id);
if (params.length > 0) {
request += `?${params.join('&')}`;
}
this.rest.makeRequest('get', request, true)
let endpoint = Constants.Endpoints.channelMessages(channel.id);
if (params.length > 0) endpoint += `?${params.join('&')}`;
this.rest.makeRequest('get', endpoint, true)
.then(resolve)
.catch(reject);
});
@@ -362,13 +288,9 @@ class RESTMethods {
updateGuildMember(member, data) {
return new Promise((resolve, reject) => {
if (data.channel) {
data.channel_id = this.rest.client.resolver.resolveChannel(data.channel).id;
}
if (data.channel) data.channel_id = this.rest.client.resolver.resolveChannel(data.channel).id;
if (data.roles) {
if (data.roles instanceof Map) {
data.roles = data.roles.array();
}
if (data.roles instanceof Collection) data.roles = data.roles.array();
data.roles = data.roles.map(role => role instanceof Role ? role.id : role);
}
@@ -379,6 +301,7 @@ class RESTMethods {
endpoint = Constants.Endpoints.stupidInconsistentGuildEndpoint(member.guild.id);
}
}
this.rest.makeRequest('patch', endpoint, true, data)
.then(resData => resolve(member.guild._updateMember(member, resData).mem))
.catch(reject);
@@ -407,9 +330,7 @@ class RESTMethods {
unbanGuildMember(guild, member) {
return new Promise((resolve, reject) => {
member = this.rest.client.resolver.resolveUser(member);
if (!member) {
throw new Error('cannot unban a user that is not a user resolvable');
}
if (!member) throw new Error('cannot unban a user that is not a user resolvable');
const listener = (eGuild, eUser) => {
if (guild.id === guild.id && member.id === eUser.id) {
this.rest.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
@@ -423,48 +344,30 @@ class RESTMethods {
getGuildBans(guild) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true)
.then(banItems => {
const bannedUsers = new Collection();
for (const banItem of banItems) {
const user = this.rest.client.dataManager.newUser(banItem.user);
bannedUsers.set(user.id, user);
}
resolve(bannedUsers);
})
.catch(reject);
this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true).then(banItems => {
const bannedUsers = new Collection();
for (const banItem of banItems) {
const user = this.rest.client.dataManager.newUser(banItem.user);
bannedUsers.set(user.id, user);
}
resolve(bannedUsers);
}).catch(reject);
});
}
updateGuildRole(role, _data) {
return new Promise((resolve, reject) => {
/*
can contain:
name, position, permissions, color, hoist
*/
const data = {};
data.name = _data.name || role.name;
data.position = _data.position || role.position;
data.color = _data.color || role.color;
if (data.color.startsWith('#')) {
data.color = parseInt(data.color.replace('#', ''), 16);
}
if (typeof _data.hoist !== 'undefined') {
data.hoist = _data.hoist;
} else {
data.hoist = role.hoist;
}
if (data.color.startsWith('#')) data.color = parseInt(data.color.replace('#', ''), 16);
data.hoist = typeof _data.hoist !== 'undefined' ? _data.hoist : role.hoist;
if (_data.permissions) {
let perms = 0;
for (let perm of _data.permissions) {
if (typeof perm === 'string') {
perm = Constants.PermissionFlags[perm];
}
if (typeof perm === 'string') perm = Constants.PermissionFlags[perm];
perms |= perm;
}
data.permissions = perms;
@@ -472,14 +375,12 @@ class RESTMethods {
data.permissions = role.permissions;
}
this.rest.makeRequest('patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data)
.then(_role => {
resolve(this.rest.client.actions.GuildRoleUpdate.handle({
role: _role,
guild_id: role.guild.id,
}).updated);
})
.catch(reject);
this.rest.makeRequest('patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data).then(_role => {
resolve(this.rest.client.actions.GuildRoleUpdate.handle({
role: _role,
guild_id: role.guild.id,
}).updated);
}).catch(reject);
});
}
@@ -526,16 +427,14 @@ class RESTMethods {
getGuildInvites(guild) {
return new Promise((resolve, reject) => {
this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true)
.then(inviteItems => {
const invites = new Collection();
for (const inviteItem of inviteItems) {
const invite = new Invite(this.rest.client, inviteItem);
invites.set(invite.code, invite);
}
resolve(invites);
})
.catch(reject);
this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true).then(inviteItems => {
const invites = new Collection();
for (const inviteItem of inviteItems) {
const invite = new Invite(this.rest.client, inviteItem);
invites.set(invite.code, invite);
}
resolve(invites);
}).catch(reject);
});
}
}

View File

@@ -3,6 +3,9 @@
* @private
*/
class RequestHandler {
/**
* @param {RESTManager} restManager The REST manager to use
*/
constructor(restManager) {
/**
* The RESTManager that instantiated this RequestHandler
@@ -12,7 +15,7 @@ class RequestHandler {
/**
* A list of requests that have yet to be processed.
* @type {Array<APIRequest>}
* @type {APIRequest[]}
*/
this.queue = [];
}
@@ -31,7 +34,7 @@ class RequestHandler {
/**
* Push a new API request into this bucket
* @param {APIRequest} request the new request to push into the queue
* @param {APIRequest} request The new request to push into the queue
*/
push(request) {
this.queue.push(request);

View File

@@ -8,7 +8,10 @@ const RequestHandler = require('./RequestHandler');
* @private
*/
class SequentialRequestHandler extends RequestHandler {
/**
* @param {RESTManager} restManager The REST manager to use
* @param {string} endpoint The endpoint to handle
*/
constructor(restManager, endpoint) {
super(restManager, endpoint);
@@ -39,8 +42,8 @@ class SequentialRequestHandler extends RequestHandler {
/**
* Performs a request then resolves a promise to indicate its readiness for a new request
* @param {APIRequest} item the item to execute
* @returns {Promise<Object, Error>}
* @param {APIRequest} item The item to execute
* @returns {Promise<?Object|Error>}
*/
execute(item) {
return new Promise(resolve => {
@@ -88,9 +91,8 @@ class SequentialRequestHandler extends RequestHandler {
handle() {
super.handle();
if (this.waiting || this.queue.length === 0 || this.globalLimit) {
return;
}
if (this.waiting || this.queue.length === 0 || this.globalLimit) return;
this.waiting = true;
const item = this.queue[0];

View File

@@ -33,9 +33,7 @@ class ClientVoiceManager {
*/
_checkPendingReady(guildID) {
const pendingRequest = this.pending.get(guildID);
if (!pendingRequest) {
throw new Error('Guild not pending');
}
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);
@@ -49,15 +47,13 @@ class ClientVoiceManager {
/**
* 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
* @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');
}
if (!pendingRequest) throw new Error('Guild not pending');
pendingRequest.token = token;
// remove the port otherwise it errors ¯\_(ツ)_/¯
pendingRequest.endpoint = endpoint.match(/([^:]*)/)[0];
@@ -66,22 +62,20 @@ class ClientVoiceManager {
/**
* 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
* @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');
}
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
* @param {VoiceChannel} channel The channel to join
* @param {Object} [options] The options to provide
*/
_sendWSJoin(channel, options = {}) {
options = mergeDefault({
@@ -98,14 +92,12 @@ class ClientVoiceManager {
/**
* Sets up a request to join a voice channel
* @param {VoiceChannel} channel the voice channel to join
* @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');
}
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) {

View File

@@ -71,14 +71,14 @@ class VoiceConnection extends EventEmitter {
/**
* Executed whenever an error occurs with the UDP/WebSocket sub-client
* @private
* @param {Error} err The error that occurred
* @param {Error} err The encountered error
*/
_onError(err) {
this._reject(err);
/**
* Emitted whenever the connection encounters a fatal error.
* @event VoiceConnection#error
* @param {Error} error the encountered error
* @param {Error} error The encountered error
*/
this.emit('error', err);
this._shutdown(err);
@@ -86,7 +86,7 @@ class VoiceConnection extends EventEmitter {
/**
* Disconnects the Client from the Voice Channel
* @param {string} [reason='user requested'] the reason of the disconnection
* @param {string} [reason='user requested'] The reason of the disconnection
*/
disconnect(reason = 'user requested') {
this.manager.client.ws.send({
@@ -107,19 +107,15 @@ class VoiceConnection extends EventEmitter {
}
_shutdown(e) {
if (!this.ready) {
return;
}
if (!this.ready) return;
this.ready = false;
this.websocket._shutdown();
this.player._shutdown();
if (this.udp) {
this.udp._shutdown();
}
if (this.udp) this.udp._shutdown();
/**
* Emit once the voice connection has disconnected.
* @event VoiceConnection#disconnected
* @param {Error} error the error, if any
* @param {Error} error The encountered error, if any
*/
this.emit('disconnected', e);
}
@@ -149,9 +145,7 @@ class VoiceConnection extends EventEmitter {
});
this.once('ready', () => {
setImmediate(() => {
for (const item of this.queue) {
this.emit(...item);
}
for (const item of this.queue) this.emit(...item);
this.queue = [];
});
});
@@ -197,21 +191,18 @@ class VoiceConnection extends EventEmitter {
/**
* Emitted whenever a user starts/stops speaking
* @event VoiceConnection#speaking
* @param {User} user the user that has started/stopped speaking
* @param {boolean} speaking whether or not the user is speaking
* @param {User} user The user that has started/stopped speaking
* @param {boolean} speaking Whether or not the user is speaking
*/
if (this.ready) {
this.emit('speaking', user, data.speaking);
} else {
this.queue.push(['speaking', user, data.speaking]);
}
if (this.ready) this.emit('speaking', user, data.speaking);
else this.queue.push(['speaking', user, data.speaking]);
guild._memberSpeakUpdate(data.user_id, data.speaking);
});
}
/**
* Play the given file in the voice connection
* @param {string} file the path to the file
* @param {string} file The path to the file
* @returns {StreamDispatcher}
* @example
* // play files natively
@@ -227,7 +218,7 @@ class VoiceConnection extends EventEmitter {
/**
* Plays and converts an audio stream in the voice connection
* @param {ReadableStream} stream the audio stream to play
* @param {ReadableStream} stream The audio stream to play
* @returns {StreamDispatcher}
* @example
* // play streams using ytdl-core
@@ -245,7 +236,7 @@ class VoiceConnection extends EventEmitter {
/**
* Plays a stream of 16-bit signed stereo PCM at 48KHz.
* @param {ReadableStream} stream the audio stream to play.
* @param {ReadableStream} stream The audio stream to play.
* @returns {StreamDispatcher}
*/
playConvertedStream(stream) {

View File

@@ -4,7 +4,6 @@ const Constants = require('../../util/Constants');
const EventEmitter = require('events').EventEmitter;
class VoiceConnectionUDPClient extends EventEmitter {
constructor(voiceConnection, data) {
super();
this.voiceConnection = voiceConnection;
@@ -38,9 +37,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
try {
this.udpSocket.close();
} catch (err) {
if (err.message !== 'Not running') {
this.emit('error', err);
}
if (err.message !== 'Not running') this.emit('error', err);
}
this.udpSocket = null;
}
@@ -55,10 +52,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
this.udpSocket.once('message', message => {
const packet = new Buffer(message);
this.localIP = '';
for (let i = 4; i < packet.indexOf(0, i); i++) {
this.localIP += String.fromCharCode(packet[i]);
}
for (let i = 4; i < packet.indexOf(0, i); i++) this.localIP += String.fromCharCode(packet[i]);
this.localPort = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
this.voiceConnection.websocket.send({
@@ -77,7 +71,6 @@ class VoiceConnectionUDPClient extends EventEmitter {
this.udpSocket.on('error', (error, message) => {
this.emit('error', { error, message });
});
this.udpSocket.on('close', error => {
this.emit('close', error);
});
@@ -86,7 +79,6 @@ class VoiceConnectionUDPClient extends EventEmitter {
blankMessage.writeUIntBE(this.data.ssrc, 0, 4);
this.send(blankMessage);
}
}
module.exports = VoiceConnectionUDPClient;

View File

@@ -26,15 +26,11 @@ class VoiceConnectionWebSocket extends EventEmitter {
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
if (this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(data));
}
_shutdown() {
if (this.ws) {
this.ws.close();
}
if (this.ws) this.ws.close();
clearInterval(this.heartbeat);
}
@@ -97,9 +93,7 @@ class VoiceConnectionWebSocket extends EventEmitter {
case Constants.VoiceOPCodes.SESSION_DESCRIPTION:
this.encryptionMode = packet.d.mode;
this.secretKey = new Uint8Array(new ArrayBuffer(packet.d.secret_key.length));
for (const index in packet.d.secret_key) {
this.secretKey[index] = packet.d.secret_key[index];
}
for (const index in packet.d.secret_key) this.secretKey[index] = packet.d.secret_key[index];
this.emit('ready', this.secretKey);
break;
case Constants.VoiceOPCodes.SPEAKING:

View File

@@ -34,10 +34,10 @@ class StreamDispatcher extends EventEmitter {
}
/**
* Emitted when the dispatcher starts/stops speaking
* @event StreamDispatcher#speaking
* @param {boolean} value whether or not the dispatcher is speaking
*/
* Emitted when the dispatcher starts/stops speaking
* @event StreamDispatcher#speaking
* @param {boolean} value Whether or not the dispatcher is speaking
*/
_setSpeaking(value) {
this.speaking = value;
this.emit('speaking', value);
@@ -62,27 +62,18 @@ class StreamDispatcher extends EventEmitter {
packetBuffer.copy(nonce, 0, 0, 12);
buffer = NaCl.secretbox(buffer, nonce, this.player.connection.data.secret);
for (let i = 0; i < buffer.length; i++) {
packetBuffer[i + 12] = buffer[i];
}
for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i];
return packetBuffer;
}
_applyVolume(buffer) {
if (this._volume === 1) {
return buffer;
}
if (this._volume === 1) return buffer;
const out = new Buffer(buffer.length);
for (let i = 0; i < buffer.length; i += 2) {
if (i >= buffer.length - 1) {
break;
}
if (i >= buffer.length - 1) break;
const uint = Math.min(32767, Math.max(-32767, Math.floor(this._volume * buffer.readInt16LE(i))));
out.writeInt16LE(uint, i);
}
@@ -95,20 +86,24 @@ class StreamDispatcher extends EventEmitter {
this._setSpeaking(false);
return;
}
const data = this.streamingData;
if (data.missed >= 5) {
this._triggerTerminalState('error', new Error('stream is not generating fast enough'));
return;
}
if (this.paused) {
data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
this.player.connection.manager.client.setTimeout(() => this._send(), data.length * 10);
return;
}
const bufferLength = 1920 * data.channels;
this._setSpeaking(true);
let buffer = this.stream.read(bufferLength);
this._setSpeaking(true);
const bufferLength = 1920 * data.channels;
let buffer = this.stream.read(bufferLength);
if (!buffer) {
data.missed++;
this.player.connection.manager.client.setTimeout(() => this._send(), data.length * 10);
@@ -132,7 +127,6 @@ class StreamDispatcher extends EventEmitter {
this._sendBuffer(buffer, data.sequence, data.timestamp);
const nextTime = data.startTime + (data.count * data.length);
this.player.connection.manager.client.setTimeout(() => this._send(), data.length + (nextTime - Date.now()));
} catch (e) {
this._triggerTerminalState('error', e);
@@ -140,33 +134,31 @@ class StreamDispatcher extends EventEmitter {
}
/**
* Emitted once the stream has ended. Attach a `once` listener to this.
* @event StreamDispatcher#end
*/
* Emitted once the stream has ended. Attach a `once` listener to this.
* @event StreamDispatcher#end
*/
_triggerEnd() {
this.emit('end');
}
/**
* Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`.
* @event StreamDispatcher#error
* @param {Error} err the error encountered
*/
* Emitted once the stream has encountered an error. Attach a `once` listener to this. Also emits `end`.
* @event StreamDispatcher#error
* @param {Error} err The encountered error
*/
_triggerError(err) {
this.emit('end');
this.emit('error', err);
}
_triggerTerminalState(state, err) {
if (this._triggered) {
return;
}
if (this._triggered) return;
/**
* Emitted when the stream wants to give debug information.
* @event StreamDispatcher#debug
* @param {string} information the debug information
*/
* Emitted when the stream wants to give debug information.
* @event StreamDispatcher#debug
* @param {string} information The debug information
*/
this.emit('debug', `triggered terminal state ${state} - stream is now dead`);
this._triggered = true;
this._setSpeaking(false);
@@ -188,17 +180,20 @@ class StreamDispatcher extends EventEmitter {
this.emit('error', 'no stream');
return;
}
this.stream.on('end', err => this._triggerTerminalState('end', err));
this.stream.on('error', err => this._triggerTerminalState('error', err));
const data = this.streamingData;
data.length = 20;
data.missed = 0;
data.startTime = Date.now();
this.stream.once('readable', () => this._send());
}
_pause(value) {
if (value) {
_setPaused(paused) {
if (paused) {
this.paused = true;
this._setSpeaking(false);
} else {
@@ -225,7 +220,7 @@ class StreamDispatcher extends EventEmitter {
/**
* Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double.
* @param {number} volume the volume that you want to set
* @param {number} volume The volume that you want to set
*/
setVolume(volume) {
this._volume = volume;
@@ -233,7 +228,7 @@ class StreamDispatcher extends EventEmitter {
/**
* Set the volume in decibels
* @param {number} db the decibels
* @param {number} db The decibels
*/
setVolumeDecibels(db) {
this._volume = Math.pow(10, db / 20);
@@ -241,7 +236,7 @@ class StreamDispatcher extends EventEmitter {
/**
* Set the volume so that a perceived value of 0.5 is half the perceived volume etc.
* @param {number} value the value for the volume
* @param {number} value The value for the volume
*/
setVolumeLogarithmic(value) {
this._volume = Math.pow(value, 1.660964);
@@ -251,14 +246,14 @@ class StreamDispatcher extends EventEmitter {
* Stops sending voice packets to the voice connection (stream may still progress however)
*/
pause() {
this._pause(true);
this._setPaused(true);
}
/**
* Resumes sending voice packets to the voice connection (may be further on in the stream than when paused)
*/
resume() {
this._pause(false);
this._setPaused(false);
}
}

View File

@@ -6,7 +6,6 @@ class NodeOpusEngine extends OpusEngine {
constructor(player) {
super(player);
try {
// eslint-disable-next-line import/no-unresolved
opus = require('node-opus');
} catch (err) {
throw err;

View File

@@ -3,10 +3,6 @@ const list = [
require('./OpusScriptEngine'),
];
exports.add = encoder => {
list.push(encoder);
};
function fetch(Encoder) {
try {
return new Encoder();
@@ -15,12 +11,14 @@ function fetch(Encoder) {
}
}
exports.add = encoder => {
list.push(encoder);
};
exports.fetch = () => {
for (const encoder of list) {
const success = fetch(encoder);
if (success) {
return success;
}
const fetched = fetch(encoder);
if (fetched) return fetched;
}
throw new Error('could not find an opus engine');
};

View File

@@ -1,17 +1,16 @@
const OpusEngine = require('./BaseOpusEngine');
let Opusscript;
let OpusScript;
class NodeOpusEngine extends OpusEngine {
constructor(player) {
super(player);
try {
// eslint-disable-next-line import/no-unresolved
Opusscript = require('opusscript');
OpusScript = require('opusscript');
} catch (err) {
throw err;
}
this.encoder = new Opusscript(48000, 2);
this.encoder = new OpusScript(48000, 2);
}
encode(buffer) {

View File

@@ -1,7 +1,6 @@
const EventEmitter = require('events').EventEmitter;
class ConverterEngine extends EventEmitter {
constructor(player) {
super();
this.player = player;
@@ -10,7 +9,6 @@ class ConverterEngine extends EventEmitter {
createConvertStream() {
return;
}
}
module.exports = ConverterEngine;

View File

@@ -1,15 +1,6 @@
const ConverterEngine = require('./ConverterEngine');
const ChildProcess = require('child_process');
function chooseCommand() {
for (const cmd of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) {
if (!ChildProcess.spawnSync(cmd, ['-h']).error) {
return cmd;
}
}
return null;
}
class FfmpegConverterEngine extends ConverterEngine {
constructor(player) {
super(player);
@@ -17,9 +8,7 @@ class FfmpegConverterEngine extends ConverterEngine {
}
handleError(encoder, err) {
if (encoder.destroy) {
encoder.destroy();
}
if (encoder.destroy) encoder.destroy();
this.emit('error', err);
}
@@ -41,4 +30,11 @@ class FfmpegConverterEngine extends ConverterEngine {
}
}
function chooseCommand() {
for (const cmd of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) {
if (!ChildProcess.spawnSync(cmd, ['-h']).error) return cmd;
}
return null;
}
module.exports = FfmpegConverterEngine;

View File

@@ -5,7 +5,6 @@ const StreamDispatcher = require('../dispatcher/StreamDispatcher');
const EventEmitter = require('events').EventEmitter;
class VoiceConnectionPlayer extends EventEmitter {
constructor(connection) {
super();
this.connection = connection;
@@ -38,9 +37,7 @@ class VoiceConnectionPlayer extends EventEmitter {
_shutdown() {
this.speaking = false;
for (const stream of this.processMap.keys()) {
this.killStream(stream);
}
for (const stream of this.processMap.keys()) this.killStream(stream);
}
killStream(stream) {
@@ -79,9 +76,7 @@ class VoiceConnectionPlayer extends EventEmitter {
}
setSpeaking(value) {
if (this.speaking === value) {
return;
}
if (this.speaking === value) return;
this.speaking = value;
this.connection.websocket.send({
op: Constants.VoiceOPCodes.SPEAKING,
@@ -100,7 +95,6 @@ class VoiceConnectionPlayer extends EventEmitter {
this.dispatcher = dispatcher;
return dispatcher;
}
}
module.exports = VoiceConnectionPlayer;

View File

@@ -2,7 +2,6 @@ const BasePlayer = require('./BasePlayer');
const fs = require('fs');
class DefaultPlayer extends BasePlayer {
playFile(file) {
return this.playStream(fs.createReadStream(file));
}

View File

@@ -11,10 +11,8 @@ class VoiceReadable extends Readable {
return;
}
$push(d) {
if (this.open) {
this.push(d);
}
_push(d) {
if (this.open) this.push(d);
}
}

View File

@@ -53,7 +53,7 @@ class VoiceReceiver extends EventEmitter {
/**
* Creates a readable stream for a user that provides opus data while the user is speaking. When the user
* stops speaking, the stream is destroyed.
* @param {UserResolvable} user the user to create the stream for
* @param {UserResolvable} user The user to create the stream for
* @returns {ReadableStream}
*/
createOpusStream(user) {
@@ -72,17 +72,13 @@ class VoiceReceiver extends EventEmitter {
/**
* Creates a readable stream for a user that provides PCM data while the user is speaking. When the user
* stops speaking, the stream is destroyed. The stream is 16-bit signed stereo PCM at 48KHz.
* @param {UserResolvable} user the user to create the stream for
* @param {UserResolvable} user The user to create the stream for
* @returns {ReadableStream}
*/
createPCMStream(user) {
user = this.connection.manager.client.resolver.resolveUser(user);
if (!user) {
throw new Error('invalid user object supplied');
}
if (this.pcmStreams.get(user.id)) {
throw new Error('there is already an existing stream for that user!');
}
if (!user) throw new Error('invalid user object supplied');
if (this.pcmStreams.get(user.id)) throw new Error('there is already an existing stream for that user!');
const stream = new Readable();
this.pcmStreams.set(user.id, stream);
return stream;
@@ -95,34 +91,30 @@ class VoiceReceiver extends EventEmitter {
/**
* Emitted whenever a voice packet cannot be decrypted
* @event VoiceReceiver#warn
* @param {string} message the warning message
* @param {string} message The warning message
*/
this.emit('warn', 'failed to decrypt voice packet');
return;
}
data = new Buffer(data);
/**
* Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM).
* @event VoiceReceiver#opus
* @param {User} user the user that is sending the buffer (is speaking)
* @param {Buffer} buffer the opus buffer
*/
if (this.opusStreams.get(user.id)) {
this.opusStreams.get(user.id).$push(data);
}
if (this.opusStreams.get(user.id)) this.opusStreams.get(user.id)._push(data);
/**
* Emitted whenever voice data is received from the voice connection. This is _always_ emitted (unlike PCM).
* @event VoiceReceiver#opus
* @param {User} user The user that is sending the buffer (is speaking)
* @param {Buffer} buffer The opus buffer
*/
this.emit('opus', user, data);
if (this.listenerCount('pcm') > 0 || this.pcmStreams.size > 0) {
/**
* Emits decoded voice data when it's received. For performance reasons, the decoding will only
* happen if there is at least one `pcm` listener on this receiver.
* @event VoiceReceiver#pcm
* @param {User} user the user that is sending the buffer (is speaking)
* @param {Buffer} buffer the decoded buffer
*/
* Emits decoded voice data when it's received. For performance reasons, the decoding will only
* happen if there is at least one `pcm` listener on this receiver.
* @event VoiceReceiver#pcm
* @param {User} user The user that is sending the buffer (is speaking)
* @param {Buffer} buffer The decoded buffer
*/
const pcm = this.connection.player.opusEncoder.decode(data);
if (this.pcmStreams.get(user.id)) {
this.pcmStreams.get(user.id).$push(pcm);
}
if (this.pcmStreams.get(user.id)) this.pcmStreams.get(user.id)._push(pcm);
this.emit('pcm', user, pcm);
}
}

View File

@@ -8,14 +8,12 @@ const PacketManager = require('./packets/WebSocketPacketManager');
* @private
*/
class WebSocketManager {
constructor(client) {
/**
* The Client that instantiated this WebSocketManager
* @type {Client}
*/
this.client = client;
this.ws = null;
/**
* A WebSocket Packet manager, it handles all the messages
* @type {PacketManager}
@@ -26,43 +24,40 @@ class WebSocketManager {
* @type {number}
*/
this.status = Constants.Status.IDLE;
/**
* The session ID of the connection, null if not yet available.
* @type {?string}
*/
this.sessionID = null;
/**
* The packet count of the client, null if not yet available.
* @type {?number}
*/
this.sequence = -1;
/**
* The gateway address for this WebSocket connection, null if not yet available.
* @type {?string}
*/
this.gateway = null;
/**
* Whether READY was emitted normally (all packets received) or not
* @type {boolean}
*/
this.normalReady = false;
}
/**
* Connects the client to a given gateway
* @param {string} gateway the gateway to connect to
*/
connect(gateway) {
this.normalReady = false;
this.status = Constants.Status.CONNECTING;
/**
* The WebSocket connection to the gateway
* @type {?WebSocket}
*/
this.ws = null;
}
/**
* Connects the client to a given gateway
* @param {string} gateway The gateway to connect to
*/
connect(gateway) {
this.normalReady = false;
this.status = Constants.Status.CONNECTING;
this.ws = new WebSocket(gateway);
this.ws.onopen = () => this.eventOpen();
this.ws.onclose = (d) => this.eventClose(d);
@@ -113,15 +108,12 @@ class WebSocketManager {
* Run whenever the gateway connections opens up
*/
eventOpen() {
if (this.reconnecting) {
this._sendResume();
} else {
this._sendNewIdentify();
}
if (this.reconnecting) this._sendResume();
else this._sendNewIdentify();
}
/**
* Sends a gatway resume packet, in cases of unexpected disconnections.
* Sends a gateway resume packet, in cases of unexpected disconnections.
*/
_sendResume() {
const payload = {
@@ -155,30 +147,23 @@ class WebSocketManager {
/**
* Run whenever the connection to the gateway is closed, it will try to reconnect the client.
* @param {Object} event the event
* @param {Object} event The received websocket data
*/
eventClose(event) {
if (event.code === 4004) {
throw Constants.Errors.BAD_LOGIN;
}
if (!this.reconnecting && event.code !== 1000) {
this.tryReconnect();
}
if (event.code === 4004) throw Constants.Errors.BAD_LOGIN;
if (!this.reconnecting && event.code !== 1000) this.tryReconnect();
}
/**
* Run whenever a message is received from the WebSocket. Returns `true` if the message
* was handled properly.
* @param {Object} event the received websocket data
* @param {Object} event The received websocket data
* @returns {boolean}
*/
eventMessage(event) {
let packet;
try {
if (event.binary) {
event.data = zlib.inflateSync(event.data).toString();
}
if (event.binary) event.data = zlib.inflateSync(event.data).toString();
packet = JSON.parse(event.data);
} catch (e) {
return this.eventError(Constants.Errors.BAD_WS_MESSAGE);
@@ -186,22 +171,19 @@ class WebSocketManager {
this.client.emit('raw', packet);
if (packet.op === 10) {
this.client.manager.setupKeepAlive(packet.d.heartbeat_interval);
}
if (packet.op === 10) this.client.manager.setupKeepAlive(packet.d.heartbeat_interval);
return this.packetManager.handle(packet);
}
/**
* Run whenever an error occurs with the WebSocket connection. Tries to reconnect
* @param {Error} err the error that occurred
* @param {Error} err The encountered error
*/
eventError(err) {
/**
* Emitted whenever the Client encounters a serious connection error
* @event Client#error
* @param {Error} error the encountered error
* @param {Error} error The encountered error
*/
this.client.emit('error', err);
this.tryReconnect();
@@ -210,7 +192,6 @@ class WebSocketManager {
_emitReady(normal = true) {
/**
* Emitted when the Client becomes ready to start working
*
* @event Client#ready
*/
this.status = Constants.Status.READY;
@@ -252,10 +233,9 @@ class WebSocketManager {
this.ws.close();
this.packetManager.handleQueue();
/**
* Emitted when the Client tries to reconnect after being disconnected
*
* @event Client#reconnecting
*/
* Emitted when the Client tries to reconnect after being disconnected
* @event Client#reconnecting
*/
this.client.emit(Constants.Events.RECONNECTING);
this.connect(this.client.ws.gateway);
}

View File

@@ -10,7 +10,6 @@ const BeforeReadyWhitelist = [
];
class WebSocketPacketManager {
constructor(websocketManager) {
this.ws = websocketManager;
this.handlers = {};
@@ -62,9 +61,7 @@ class WebSocketPacketManager {
}
setSequence(s) {
if (s && s > this.ws.sequence) {
this.ws.sequence = s;
}
if (s && s > this.ws.sequence) this.ws.sequence = s;
}
handle(packet) {
@@ -93,13 +90,9 @@ class WebSocketPacketManager {
}
}
if (this.handlers[packet.t]) {
return this.handlers[packet.t].handle(packet);
}
if (this.handlers[packet.t]) return this.handlers[packet.t].handle(packet);
return false;
}
}
module.exports = WebSocketPacketManager;

View File

@@ -1,5 +1,4 @@
class AbstractHandler {
constructor(packetManager) {
this.packetManager = packetManager;
}

View File

@@ -3,25 +3,18 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class ChannelCreateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const response = client.actions.ChannelCreate.handle(data);
if (response.channel) {
client.emit(Constants.Events.CHANNEL_CREATE, response.channel);
}
if (response.channel) client.emit(Constants.Events.CHANNEL_CREATE, response.channel);
}
}
/**
* Emitted whenever a Channel is created.
*
* @event Client#channelCreate
* @param {Channel} channel The channel that was created
*/
* Emitted whenever a Channel is created.
* @event Client#channelCreate
* @param {Channel} channel The channel that was created
*/
module.exports = ChannelCreateHandler;

View File

@@ -3,25 +3,18 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class ChannelDeleteHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const response = client.actions.ChannelDelete.handle(data);
if (response.channel) {
client.emit(Constants.Events.CHANNEL_DELETE, response.channel);
}
if (response.channel) client.emit(Constants.Events.CHANNEL_DELETE, response.channel);
}
}
/**
* Emitted whenever a Channel is deleted.
*
* @event Client#channelDelete
* @param {Channel} channel The channel that was deleted
*/
* Emitted whenever a Channel is deleted.
* @event Client#channelDelete
* @param {Channel} channel The channel that was deleted
*/
module.exports = ChannelDeleteHandler;

View File

@@ -8,31 +8,24 @@ const Constants = require('../../../../util/Constants');
d:
{ last_pin_timestamp: '2016-08-28T17:37:13.171774+00:00',
channel_id: '314866471639044027' } }
*/
*/
class ChannelPinsUpdate extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const channel = client.channels.get(data.channel_id);
const time = new Date(data.last_pin_timestamp);
if (channel && time) {
client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time);
}
if (channel && time) client.emit(Constants.Events.CHANNEL_PINS_UPDATE, channel, time);
}
}
/**
* Emitted whenever the pins of a Channel are updated. Due to the nature of the WebSocket event, not much information
* can be provided easily here - you need to manually check the pins yourself.
*
* @event Client#channelPinsUpdate
* @param {Channel} channel The channel that the pins update occured in
* @param {Date} time the time of the pins update
*/
* Emitted whenever the pins of a Channel are updated. Due to the nature of the WebSocket event, not much information
* can be provided easily here - you need to manually check the pins yourself.
* @event Client#channelPinsUpdate
* @param {Channel} channel The channel that the pins update occured in
* @param {Date} time The time of the pins update
*/
module.exports = ChannelPinsUpdate;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class ChannelUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.ChannelUpdate.handle(data);
}
}
module.exports = ChannelUpdateHandler;

View File

@@ -4,27 +4,20 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class GuildBanAddHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
const user = client.users.get(data.user.id);
if (guild && user) {
client.emit(Constants.Events.GUILD_BAN_ADD, guild, user);
}
if (guild && user) client.emit(Constants.Events.GUILD_BAN_ADD, guild, user);
}
}
/**
* Emitted whenever a member is banned from a guild.
*
* @event Client#guildBanAdd
* @param {Guild} guild The guild that the ban occurred in
* @param {User} user The user that was banned
*/
* Emitted whenever a member is banned from a guild.
* @event Client#guildBanAdd
* @param {Guild} guild The guild that the ban occurred in
* @param {User} user The user that was banned
*/
module.exports = GuildBanAddHandler;

View File

@@ -3,22 +3,18 @@
const AbstractHandler = require('./AbstractHandler');
class GuildBanRemoveHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildBanRemove.handle(data);
}
}
/**
* Emitted whenever a member is unbanned from a guild.
*
* @event Client#guildBanRemove
* @param {Guild} guild The guild that the unban occurred in
* @param {User} user The user that was unbanned
*/
* Emitted whenever a member is unbanned from a guild.
* @event Client#guildBanRemove
* @param {Guild} guild The guild that the unban occurred in
* @param {User} user The user that was unbanned
*/
module.exports = GuildBanRemoveHandler;

View File

@@ -1,13 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildCreateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.id);
if (guild) {
if (!guild.available && !data.unavailable) {
// a newly available guild
@@ -19,7 +17,6 @@ class GuildCreateHandler extends AbstractHandler {
client.dataManager.newGuild(data);
}
}
}
module.exports = GuildCreateHandler;

View File

@@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class GuildDeleteHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const response = client.actions.GuildDelete.handle(data);
if (response.guild) {
client.emit(Constants.Events.GUILD_DELETE, response.guild);
}
if (response.guild) client.emit(Constants.Events.GUILD_DELETE, response.guild);
}
}
/**
* Emitted whenever a Guild is deleted/left.
*
* @event Client#guildDelete
* @param {Guild} guild The guild that was deleted
*/
* Emitted whenever a Guild is deleted/left.
* @event Client#guildDelete
* @param {Guild} guild The guild that was deleted
*/
module.exports = GuildDeleteHandler;

View File

@@ -3,19 +3,15 @@
const AbstractHandler = require('./AbstractHandler');
class GuildMemberAddHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
if (guild) {
guild.memberCount++;
guild._addMember(data);
}
}
}
module.exports = GuildMemberAddHandler;

View File

@@ -3,14 +3,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildMemberRemoveHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildMemberRemove.handle(data);
}
}
module.exports = GuildMemberRemoveHandler;

View File

@@ -3,21 +3,16 @@
const AbstractHandler = require('./AbstractHandler');
class GuildMemberUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
if (guild) {
const member = guild.members.get(data.user.id);
if (member) {
guild._updateMember(member, data);
}
if (member) guild._updateMember(member, data);
}
}
}
module.exports = GuildMemberUpdateHandler;

View File

@@ -4,31 +4,26 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class GuildMembersChunkHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
const members = [];
if (guild) {
for (const member of data.members) {
members.push(guild._addMember(member, true));
}
for (const member of data.members) members.push(guild._addMember(member, true));
}
guild._checkChunks();
client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, guild, members);
}
}
/**
* Emitted whenever a chunk of Guild members is received
*
* @event Client#guildMembersChunk
* @param {Guild} guild The guild that the chunks relate to
* @param {Array<GuildMember>} members The members in the chunk
*/
* Emitted whenever a chunk of Guild members is received
* @event Client#guildMembersChunk
* @param {Guild} guild The guild that the chunks relate to
* @param {GuildMember[]} members The members in the chunk
*/
module.exports = GuildMembersChunkHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildRoleCreateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildRoleCreate.handle(data);
}
}
module.exports = GuildRoleCreateHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildRoleDeleteHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildRoleDelete.handle(data);
}
}
module.exports = GuildRoleDeleteHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildRoleUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildRoleUpdate.handle(data);
}
}
module.exports = GuildRoleUpdateHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildSyncHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildSync.handle(data);
}
}
module.exports = GuildSyncHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class GuildUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.GuildUpdate.handle(data);
}
}
module.exports = GuildUpdateHandler;

View File

@@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class MessageCreateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const response = client.actions.MessageCreate.handle(data);
if (response.message) {
client.emit(Constants.Events.MESSAGE_CREATE, response.message);
}
if (response.message) client.emit(Constants.Events.MESSAGE_CREATE, response.message);
}
}
/**
* Emitted whenever a message is created
*
* @event Client#message
* @param {Message} message The created message
*/
* Emitted whenever a message is created
* @event Client#message
* @param {Message} message The created message
*/
module.exports = MessageCreateHandler;

View File

@@ -2,25 +2,18 @@ const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class MessageDeleteHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const response = client.actions.MessageDelete.handle(data);
if (response.message) {
client.emit(Constants.Events.MESSAGE_DELETE, response.message);
}
if (response.message) client.emit(Constants.Events.MESSAGE_DELETE, response.message);
}
}
/**
* Emitted whenever a message is deleted
*
* @event Client#messageDelete
* @param {Message} message The deleted message
*/
* Emitted whenever a message is deleted
* @event Client#messageDelete
* @param {Message} message The deleted message
*/
module.exports = MessageDeleteHandler;

View File

@@ -1,21 +1,17 @@
const AbstractHandler = require('./AbstractHandler');
class MessageDeleteBulkHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.MessageDeleteBulk.handle(data);
}
}
/**
* Emitted whenever a messages are deleted in bulk
*
* @event Client#messageDeleteBulk
* @param {Collection<string, Message>} messages The deleted messages, mapped by their ID
*/
* Emitted whenever messages are deleted in bulk
* @event Client#messageDeleteBulk
* @param {Collection<string, Message>} messages The deleted messages, mapped by their ID
*/
module.exports = MessageDeleteBulkHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class MessageUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.MessageUpdate.handle(data);
}
}
module.exports = MessageUpdateHandler;

View File

@@ -3,21 +3,16 @@ const Constants = require('../../../../util/Constants');
const cloneObject = require('../../../../util/CloneObject');
class PresenceUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
let user = client.users.get(data.user.id);
const guild = client.guilds.get(data.guild_id);
function makeUser(newUser) {
return client.dataManager.newUser(newUser);
}
// step 1
if (!user) {
if (data.user.username) {
user = makeUser(data.user);
user = client.dataManager.newUser(data.user);
} else {
return;
}
@@ -39,7 +34,6 @@ class PresenceUpdateHandler extends AbstractHandler {
data.user.username = data.user.username || user.username;
data.user.id = data.user.id || user.id;
data.user.discriminator = data.user.discriminator || user.discriminator;
// comment out avatar patching as it causes bugs (see #297)
// data.user.avatar = data.user.avatar || user.avatar;
data.user.status = data.status || user.status;
@@ -58,24 +52,20 @@ class PresenceUpdateHandler extends AbstractHandler {
client.emit(Constants.Events.PRESENCE_UPDATE, oldUser, user);
}
}
}
/**
* Emitted whenever a user changes one of their details or starts/stop playing a game
*
* @event Client#presenceUpdate
* @param {User} oldUser the user before the presence update
* @param {User} newUser the user after the presence update
*/
* Emitted whenever a user changes one of their details or starts/stop playing a game
* @event Client#presenceUpdate
* @param {User} oldUser The user before the presence update
* @param {User} newUser The user after the presence update
*/
/**
* Emitted whenever a member becomes available in a large Guild
*
* @event Client#guildMemberAvailable
* @param {Guild} guild The guild that the member became available in
* @param {GuildMember} member the member that became available
*/
* Emitted whenever a member becomes available in a large Guild
* @event Client#guildMemberAvailable
* @param {Guild} guild The guild that the member became available in
* @param {GuildMember} member The member that became available
*/
module.exports = PresenceUpdateHandler;

View File

@@ -4,40 +4,28 @@ const getStructure = name => require(`../../../../structures/${name}`);
const ClientUser = getStructure('ClientUser');
class ReadyHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const clientUser = new ClientUser(client, data.user);
client.user = clientUser;
client.readyTime = Date.now();
client.users.set(clientUser.id, clientUser);
for (const guild of data.guilds) {
client.dataManager.newGuild(guild);
}
for (const privateDM of data.private_channels) {
client.dataManager.newChannel(privateDM);
}
if (!client.user.bot) {
client.setInterval(client.syncGuilds.bind(client), 30000);
}
for (const guild of data.guilds) client.dataManager.newGuild(guild);
for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM);
if (!client.user.bot) client.setInterval(client.syncGuilds.bind(client), 30000);
client.once('ready', client.syncGuilds.bind(client));
client.setTimeout(() => {
if (!client.ws.normalReady) {
client.ws._emitReady(false);
}
if (!client.ws.normalReady) client.ws._emitReady(false);
}, 1200 * data.guilds.length);
this.packetManager.ws.sessionID = data.session_id;
this.packetManager.ws.checkIfReady();
}
}
module.exports = ReadyHandler;

View File

@@ -1,28 +1,10 @@
const AbstractHandler = require('./AbstractHandler');
const Constants = require('../../../../util/Constants');
class TypingData {
constructor(since, lastTimestamp, _timeout) {
this.since = since;
this.lastTimestamp = lastTimestamp;
this._timeout = _timeout;
}
resetTimeout(_timeout) {
clearTimeout(this._timeout);
this._timeout = _timeout;
}
get elapsedTime() {
return Date.now() - this.since;
}
}
class TypingStartHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
const channel = client.channels.get(data.channel_id);
const user = client.users.get(data.user_id);
const timestamp = new Date(data.timestamp * 1000);
@@ -46,23 +28,37 @@ class TypingStartHandler extends AbstractHandler {
}
}
}
}
class TypingData {
constructor(since, lastTimestamp, _timeout) {
this.since = since;
this.lastTimestamp = lastTimestamp;
this._timeout = _timeout;
}
resetTimeout(_timeout) {
clearTimeout(this._timeout);
this._timeout = _timeout;
}
get elapsedTime() {
return Date.now() - this.since;
}
}
/**
* Emitted whenever a user starts typing in a channel
*
* @event Client#typingStart
* @param {Channel} channel the channel the user started typing in
* @param {User} user the user that started typing
*/
* Emitted whenever a user starts typing in a channel
* @event Client#typingStart
* @param {Channel} channel The channel the user started typing in
* @param {User} user The user that started typing
*/
/**
* Emitted whenever a user stops typing in a channel
*
* @event Client#typingStop
* @param {Channel} channel the channel the user stopped typing in
* @param {User} user the user that stopped typing
*/
* Emitted whenever a user stops typing in a channel
* @event Client#typingStop
* @param {Channel} channel The channel the user stopped typing in
* @param {User} user The user that stopped typing
*/
module.exports = TypingStartHandler;

View File

@@ -1,14 +1,11 @@
const AbstractHandler = require('./AbstractHandler');
class UserUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
client.actions.UserUpdate.handle(data);
}
}
module.exports = UserUpdateHandler;

View File

@@ -9,16 +9,13 @@ const AbstractHandler = require('./AbstractHandler');
*/
class VoiceServerUpdate extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const data = packet.d;
if (client.voice.pending.has(data.guild_id)) {
client.voice._receivedVoiceServer(data.guild_id, data.token, data.endpoint);
}
}
}
module.exports = VoiceServerUpdate;

View File

@@ -4,12 +4,11 @@ const Constants = require('../../../../util/Constants');
const cloneObject = require('../../../../util/CloneObject');
class VoiceStateUpdateHandler extends AbstractHandler {
handle(packet) {
const data = packet.d;
const client = this.packetManager.client;
const guild = client.guilds.get(data.guild_id);
const data = packet.d;
const guild = client.guilds.get(data.guild_id);
if (guild) {
const member = guild.members.get(data.user_id);
if (member) {
@@ -19,18 +18,14 @@ class VoiceStateUpdateHandler extends AbstractHandler {
}
// if the member left the voice channel, unset their speaking property
if (!data.channel_id) {
member.speaking = null;
}
if (!data.channel_id) member.speaking = null;
if (client.voice.pending.has(guild.id) && member.user.id === client.user.id && data.channel_id) {
client.voice._receivedVoiceStateUpdate(data.guild_id, data.session_id);
}
const newChannel = client.channels.get(data.channel_id);
if (newChannel) {
newChannel.members.set(member.user.id, member);
}
if (newChannel) newChannel.members.set(member.user.id, member);
member.serverMute = data.mute;
member.serverDeaf = data.deaf;
@@ -42,15 +37,13 @@ class VoiceStateUpdateHandler extends AbstractHandler {
}
}
}
}
/**
* Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
*
* @event Client#voiceStateUpdate
* @param {GuildMember} oldMember the member before the voice state update
* @param {GuildMember} newMember the member before the voice state update
*/
* Emitted whenever a user changes voice state - e.g. joins/leaves a channel, mutes/unmutes.
* @event Client#voiceStateUpdate
* @param {GuildMember} oldMember The member before the voice state update
* @param {GuildMember} newMember The member after the voice state update
*/
module.exports = VoiceStateUpdateHandler;