mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 00:23:30 +01:00
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:
committed by
Amish Shah
parent
5a9c42061f
commit
0b908f5bce
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class ActionsManager {
|
||||
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class ChannelCreateAction extends Action {
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const channel = client.dataManager.newChannel(data);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class ChannelDeleteAction extends Action {
|
||||
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildSync extends Action {
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,6 @@ const Action = require('./Action');
|
||||
const Message = require('../../structures/Message');
|
||||
|
||||
class MessageCreateAction extends Action {
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class MessageDeleteAction extends Action {
|
||||
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class UserGetAction extends Action {
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const user = client.dataManager.newUser(data);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ const APIRequest = require('./APIRequest');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class RESTManager {
|
||||
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
this.handlers = {};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,6 @@ const BasePlayer = require('./BasePlayer');
|
||||
const fs = require('fs');
|
||||
|
||||
class DefaultPlayer extends BasePlayer {
|
||||
|
||||
playFile(file) {
|
||||
return this.playStream(fs.createReadStream(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
class AbstractHandler {
|
||||
|
||||
constructor(packetManager) {
|
||||
this.packetManager = packetManager;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,9 +10,7 @@ class Channel {
|
||||
this.client = client;
|
||||
this.typingMap = {};
|
||||
this.typingTimeouts = [];
|
||||
if (guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
if (guild) this.guild = guild;
|
||||
/**
|
||||
* The type of the channel, either:
|
||||
* * `dm` - a DM channel
|
||||
@@ -22,9 +20,7 @@ class Channel {
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = null;
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
|
||||
@@ -25,7 +25,7 @@ class ClientUser extends User {
|
||||
* Set the username of the logged in Client.
|
||||
* <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
|
||||
* every hour. Use this sparingly!</info>
|
||||
* @param {string} username the new username
|
||||
* @param {string} username The new username
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // set username
|
||||
@@ -40,7 +40,7 @@ class ClientUser extends User {
|
||||
/**
|
||||
* If this user is a "self bot" or logged in using a normal user's details (which should be avoided), you can set the
|
||||
* email here.
|
||||
* @param {string} email the new email
|
||||
* @param {string} email The new email
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // set email
|
||||
@@ -55,7 +55,7 @@ class ClientUser extends User {
|
||||
/**
|
||||
* If this user is a "self bot" or logged in using a normal user's details (which should be avoided), you can set the
|
||||
* password here.
|
||||
* @param {string} password the new password
|
||||
* @param {string} password The new password
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // set password
|
||||
@@ -69,7 +69,7 @@ class ClientUser extends User {
|
||||
|
||||
/**
|
||||
* Set the avatar of the logged in Client.
|
||||
* @param {Base64Resolvable} avatar the new avatar
|
||||
* @param {Base64Resolvable} avatar The new avatar
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // set avatar
|
||||
@@ -83,9 +83,9 @@ class ClientUser extends User {
|
||||
|
||||
/**
|
||||
* Set the status and playing game of the logged in client.
|
||||
* @param {string} [status] the status, can be `online` or `idle`.
|
||||
* @param {string|Object} [game] the game that is being played
|
||||
* @returns {Promise<ClientUser, Error>}
|
||||
* @param {string} [status] The status, can be `online` or `idle`
|
||||
* @param {string|Object} [game] The game that is being played
|
||||
* @returns {Promise<ClientUser>}
|
||||
* @example
|
||||
* // set status
|
||||
* client.user.setStatus('status', 'game')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const User = require('./User');
|
||||
const Channel = require('./Channel');
|
||||
const TextBasedChannel = require('./interface/TextBasedChannel');
|
||||
const User = require('./User');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Collection = require('../util/Collection');
|
||||
const Constants = require('../util/Constants');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Represents a Custom Emoji
|
||||
@@ -44,16 +44,14 @@ class Emoji {
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of roles this emoji is active for (empty if all). Mapped by role ID.
|
||||
* A collection of roles this emoji is active for (empty if all), mapped by role ID.
|
||||
* @type {Collection<string, Role>}
|
||||
* @readonly
|
||||
*/
|
||||
get roles() {
|
||||
const roles = new Collection();
|
||||
for (const role of this.roleIDS) {
|
||||
if (this.guild.roles.get(role)) {
|
||||
roles.set(role, this.guild.roles.get(role));
|
||||
}
|
||||
if (this.guild.roles.get(role)) roles.set(role, this.guild.roles.get(role));
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -28,31 +28,18 @@ class EvaluatedPermissions {
|
||||
for (const permissionName in Constants.PermissionFlags) {
|
||||
serializedPermissions[permissionName] = this.hasPermission(permissionName);
|
||||
}
|
||||
|
||||
return serializedPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a user has a certain permission, e.g. `READ_MESSAGES`.
|
||||
* @param {string} permission the permission to check for
|
||||
* @param {boolean} [explicit=false] whether the user should explicitly have the permission.
|
||||
* Checks whether the user has a certain permission, e.g. `READ_MESSAGES`.
|
||||
* @param {PermissionResolvable} permission The permission to check for
|
||||
* @param {boolean} [explicit=false] Whether to require the user to explicitly have the exact permission
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasPermission(permission, explicit = false) {
|
||||
if (typeof permission === 'string') {
|
||||
permission = Constants.PermissionFlags[permission];
|
||||
}
|
||||
|
||||
if (!permission) {
|
||||
throw Constants.Errors.NOT_A_PERMISSION;
|
||||
}
|
||||
|
||||
if (!explicit) {
|
||||
if ((this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
permission = this.member.client.resolver.resolvePermission(permission);
|
||||
if (!explicit && (this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) return true;
|
||||
return (this.permissions & permission) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Channel = require('./Channel');
|
||||
const TextBasedChannel = require('./interface/TextBasedChannel');
|
||||
const Collection = require('../util/Collection');
|
||||
const arraysEqual = require('../util/ArraysEqual');
|
||||
|
||||
/*
|
||||
{ type: 3,
|
||||
@@ -24,49 +25,31 @@ const Collection = require('../util/Collection');
|
||||
icon: null }
|
||||
*/
|
||||
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (const itemInd in a) {
|
||||
const item = a[itemInd];
|
||||
const ind = b.indexOf(item);
|
||||
if (ind) {
|
||||
b.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return b.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Group DM on Discord
|
||||
* @extends {Channel}
|
||||
* @implements {TextBasedChannel}
|
||||
*/
|
||||
class GroupDMChannel extends Channel {
|
||||
|
||||
constructor(client, data) {
|
||||
super(client, data);
|
||||
|
||||
this.messages = new Collection();
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
const base = other &&
|
||||
const equal = other &&
|
||||
this.id === other.id &&
|
||||
this.name === other.name &&
|
||||
this.icon === other.icon &&
|
||||
this.owner.id === other.owner_id;
|
||||
|
||||
if (base) {
|
||||
if (equal) {
|
||||
const thisIDs = this.recipients.array().map(r => r.id);
|
||||
const otherIDs = other.recipients.map(r => r.id);
|
||||
return arraysEqual(thisIDs, otherIDs);
|
||||
}
|
||||
|
||||
return base;
|
||||
return equal;
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -86,6 +69,7 @@ class GroupDMChannel extends Channel {
|
||||
this.recipients.set(user.id, user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this Group DM, can be null if one isn't set.
|
||||
* @type {string}
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
const User = require('./User');
|
||||
const Role = require('./Role');
|
||||
const Emoji = require('./Emoji');
|
||||
const GuildMember = require('./GuildMember');
|
||||
const Constants = require('../util/Constants');
|
||||
const cloneObject = require('../util/CloneObject');
|
||||
const Role = require('./Role');
|
||||
const Collection = require('../util/Collection');
|
||||
const Emoji = require('./Emoji');
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (const itemInd in a) {
|
||||
const item = a[itemInd];
|
||||
const ind = b.indexOf(item);
|
||||
if (ind) {
|
||||
b.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return b.length === 0;
|
||||
}
|
||||
const cloneObject = require('../util/CloneObject');
|
||||
const arraysEqual = require('../util/ArraysEqual');
|
||||
|
||||
/**
|
||||
* Represents a Guild (or a Server) on Discord.
|
||||
@@ -50,9 +36,7 @@ class Guild {
|
||||
*/
|
||||
this.roles = new Collection();
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
if (!data) return;
|
||||
|
||||
if (data.unavailable) {
|
||||
/**
|
||||
@@ -100,12 +84,11 @@ class Guild {
|
||||
this.channels.get(voiceState.channel_id).members.set(member.user.id, member);
|
||||
}
|
||||
/**
|
||||
* Emitted whenever a user joins a guild.
|
||||
*
|
||||
* @event Client#guildMemberAdd
|
||||
* @param {Guild} guild the guild that the user has joined
|
||||
* @param {GuildMember} member the member that has joined
|
||||
*/
|
||||
* Emitted whenever a user joins a guild.
|
||||
* @event Client#guildMemberAdd
|
||||
* @param {Guild} guild The guild that the user has joined
|
||||
* @param {GuildMember} member The member that has joined
|
||||
*/
|
||||
if (this.client.ws.status === Constants.Status.READY && !noEvent) {
|
||||
this.client.emit(Constants.Events.GUILD_MEMBER_ADD, this, member);
|
||||
}
|
||||
@@ -117,25 +100,22 @@ class Guild {
|
||||
_updateMember(member, data) {
|
||||
const oldMember = cloneObject(member);
|
||||
|
||||
if (data.roles) {
|
||||
member._roles = data.roles;
|
||||
} else {
|
||||
member.nickname = data.nick;
|
||||
}
|
||||
if (data.roles) member._roles = data.roles;
|
||||
else member.nickname = data.nick;
|
||||
|
||||
const notSame = member.nickname !== oldMember.nickname && !arraysEqual(member._roles, oldMember._roles);
|
||||
|
||||
if (this.client.ws.status === Constants.Status.READY && notSame) {
|
||||
/**
|
||||
* Emitted whenever a Guild Member changes - i.e. new role, removed role, nickname
|
||||
*
|
||||
* @event Client#guildMemberUpdate
|
||||
* @param {Guild} guild the guild that the update affects
|
||||
* @param {GuildMember} oldMember the member before the update
|
||||
* @param {GuildMember} newMember the member after the update
|
||||
*/
|
||||
* Emitted whenever a Guild Member changes - i.e. new role, removed role, nickname
|
||||
* @event Client#guildMemberUpdate
|
||||
* @param {Guild} guild The guild that the update affects
|
||||
* @param {GuildMember} oldMember The member before the update
|
||||
* @param {GuildMember} newMember The member after the update
|
||||
*/
|
||||
this.client.emit(Constants.Events.GUILD_MEMBER_UPDATE, this, oldMember, member);
|
||||
}
|
||||
|
||||
return {
|
||||
old: oldMember,
|
||||
mem: member,
|
||||
@@ -163,7 +143,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Returns the GuildMember form of a User object, if the User is present in the guild.
|
||||
* @param {UserResolvable} user the user that you want to obtain the GuildMember of.
|
||||
* @param {UserResolvable} user The user that you want to obtain the GuildMember of
|
||||
* @returns {GuildMember|null}
|
||||
* @example
|
||||
* // get the guild member of a user
|
||||
@@ -177,11 +157,11 @@ class Guild {
|
||||
* Whether this Guild equals another Guild. It compares all properties, so for most operations
|
||||
* it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often
|
||||
* what most users need.
|
||||
* @param {Guild} guild the guild to compare
|
||||
* @param {Guild} guild The guild to compare
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(guild) {
|
||||
let base =
|
||||
let equal =
|
||||
guild &&
|
||||
this.id === guild.id &&
|
||||
this.available === !guild.unavailable &&
|
||||
@@ -196,17 +176,15 @@ class Guild {
|
||||
this.verificationLevel === guild.verification_level &&
|
||||
this.embedEnabled === guild.embed_enabled;
|
||||
|
||||
if (base) {
|
||||
if (equal) {
|
||||
if (this.embedChannel) {
|
||||
if (this.embedChannel.id !== guild.embed_channel_id) {
|
||||
base = false;
|
||||
}
|
||||
if (this.embedChannel.id !== guild.embed_channel_id) equal = false;
|
||||
} else if (guild.embed_channel_id) {
|
||||
base = false;
|
||||
equal = false;
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
return equal;
|
||||
}
|
||||
|
||||
_memberSpeakUpdate(user, speaking) {
|
||||
@@ -216,8 +194,8 @@ class Guild {
|
||||
/**
|
||||
* Emitted once a Guild Member starts/stops speaking
|
||||
* @event Client#guildMemberSpeaking
|
||||
* @param {GuildMember} member the member that started/stopped speaking
|
||||
* @param {boolean} speaking whether or not the member is speaking
|
||||
* @param {GuildMember} member The member that started/stopped speaking
|
||||
* @param {boolean} speaking Whether or not the member is speaking
|
||||
*/
|
||||
this.client.emit(Constants.Events.GUILD_MEMBER_SPEAKING, member, speaking);
|
||||
}
|
||||
@@ -225,7 +203,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Sets up the Guild
|
||||
* @param {*} data the raw data of the guild
|
||||
* @param {*} data The raw data of the guild
|
||||
* @private
|
||||
*/
|
||||
setup(data) {
|
||||
@@ -264,12 +242,12 @@ class Guild {
|
||||
this.icon = data.icon;
|
||||
/**
|
||||
* An array of guild features.
|
||||
* @type {Array<Object>}
|
||||
* @type {Object[]}
|
||||
*/
|
||||
this.features = data.features;
|
||||
/**
|
||||
* An array of guild emojis.
|
||||
* @type {Array<Object>}
|
||||
* @type {Object[]}
|
||||
*/
|
||||
this.emojis = new Collection();
|
||||
for (const emoji of data.emojis) {
|
||||
@@ -351,6 +329,7 @@ class Guild {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The date at which the logged-in client joined the guild.
|
||||
* @type {Date}
|
||||
@@ -361,9 +340,9 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Creates a new Channel in the Guild.
|
||||
* @param {string} name the name of the new channel.
|
||||
* @param {string} type the type of the new channel, either `text` or `voice`.
|
||||
* @returns {Promise<TextChannel|VoiceChannel, Error>}
|
||||
* @param {string} name The name of the new channel
|
||||
* @param {string} type The type of the new channel, either `text` or `voice`
|
||||
* @returns {Promise<TextChannel|VoiceChannel>}
|
||||
* @example
|
||||
* // create a new text channel
|
||||
* guild.createChannel('new general', 'text')
|
||||
@@ -376,7 +355,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Creates a new role in the guild, as of now this is just a blank role.
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // create a new role
|
||||
* guild.createRole()
|
||||
@@ -389,7 +368,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Causes the Client to leave the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // leave a guild
|
||||
* guild.leave()
|
||||
@@ -402,7 +381,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Causes the Client to delete the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // delete a guild
|
||||
* guild.delete()
|
||||
@@ -415,8 +394,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Updates the Guild with new information - e.g. a new name.
|
||||
* @param {GuildEditData} data the data to update the guild with.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {GuildEditData} data The data to update the guild with
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // set the guild name and region
|
||||
* guild.edit({
|
||||
@@ -432,8 +411,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Edit the name of the Guild.
|
||||
* @param {string} name the new name of the Guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {string} name The new name of the Guild
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild name
|
||||
* guild.setName('Discord Guild')
|
||||
@@ -446,8 +425,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Edit the region of the Guild.
|
||||
* @param {Region} region the new region of the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {Region} region The new region of the guild.
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild region
|
||||
* guild.setRegion('london')
|
||||
@@ -460,8 +439,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Edit the verification level of the Guild.
|
||||
* @param {VerificationLevel} verificationLevel the new verification level of the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {VerificationLevel} verificationLevel The new verification level of the guild
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild verification level
|
||||
* guild.setVerificationLevel(1)
|
||||
@@ -474,8 +453,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Edit the AFK channel of the Guild.
|
||||
* @param {GuildChannelResolvable} afkChannel the new AFK channel.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {GuildChannelResolvable} afkChannel The new AFK channel
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild AFK channel
|
||||
* guild.setAFKChannel(channel)
|
||||
@@ -488,8 +467,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Edit the AFK timeout of the Guild.
|
||||
* @param {number} afkTimeout the time in seconds that a user must be idle to be considered AFK.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {number} afkTimeout The time in seconds that a user must be idle to be considered AFK
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild AFK channel
|
||||
* guild.setAFKTimeout(60)
|
||||
@@ -502,8 +481,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Set a new Guild Icon.
|
||||
* @param {Base64Resolvable} icon the new icon of the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {Base64Resolvable} icon The new icon of the guild
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild icon
|
||||
* guild.setIcon(fs.readFileSync('./icon.png'))
|
||||
@@ -516,8 +495,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Sets a new owner of the Guild.
|
||||
* @param {GuildMemberResolvable} owner the new owner of the Guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {GuildMemberResolvable} owner The new owner of the Guild
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild owner
|
||||
* guild.setOwner(guilds.members[0])
|
||||
@@ -530,8 +509,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Set a new Guild Splash Logo.
|
||||
* @param {Base64Resolvable} splash the new splash screen of the guild.
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @param {Base64Resolvable} splash The new splash screen of the guild
|
||||
* @returns {Promise<Guild>}
|
||||
* @example
|
||||
* // edit the guild splash
|
||||
* guild.setIcon(fs.readFileSync('./splash.png'))
|
||||
@@ -544,8 +523,8 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Unbans a member from the Guild
|
||||
* @param {UserResolvable} member the member to unban
|
||||
* @returns {Promise<User, Error>}
|
||||
* @param {UserResolvable} member The member to unban
|
||||
* @returns {Promise<User>}
|
||||
* @example
|
||||
* // unban a member
|
||||
* guild.unban('123123123123')
|
||||
@@ -558,7 +537,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Fetch a Collection of banned users in this Guild.
|
||||
* @returns {Promise<Collection<string, User>, Error>}
|
||||
* @returns {Promise<Collection<string, User>>}
|
||||
*/
|
||||
fetchBans() {
|
||||
return this.client.rest.methods.getGuildBans(this);
|
||||
@@ -566,7 +545,7 @@ class Guild {
|
||||
|
||||
/**
|
||||
* Fetch a Collection of invites to this Guild. Resolves with a Collection mapping invites by their codes.
|
||||
* @returns {Promise<Collection<string, Invite>, Error>}
|
||||
* @returns {Promise<Collection<string, Invite>>}
|
||||
*/
|
||||
fetchInvites() {
|
||||
return this.client.rest.methods.getGuildInvites(this);
|
||||
@@ -576,7 +555,7 @@ class Guild {
|
||||
* Fetches all the members in the Guild, even if they are offline. If the Guild has less than 250 members,
|
||||
* this should not be necessary.
|
||||
* @param {string} [query=''] An optional query to provide when fetching members
|
||||
* @returns {Promise<Guild, Error>}
|
||||
* @returns {Promise<Guild>}
|
||||
*/
|
||||
fetchMembers(query = '') {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -607,9 +586,7 @@ class Guild {
|
||||
* @readonly
|
||||
*/
|
||||
get iconURL() {
|
||||
if (!this.icon) {
|
||||
return null;
|
||||
}
|
||||
if (!this.icon) return null;
|
||||
return Constants.Endpoints.guildIcon(this.id, this.icon);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,10 @@
|
||||
const Channel = require('./Channel');
|
||||
const PermissionOverwrites = require('./PermissionOverwrites');
|
||||
const Role = require('./Role');
|
||||
const PermissionOverwrites = require('./PermissionOverwrites');
|
||||
const EvaluatedPermissions = require('./EvaluatedPermissions');
|
||||
const Constants = require('../util/Constants');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (const itemInd in a) {
|
||||
const item = a[itemInd];
|
||||
const ind = b.indexOf(item);
|
||||
if (ind) {
|
||||
b.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return b.length === 0;
|
||||
}
|
||||
const arraysEqual = require('../util/ArraysEqual');
|
||||
|
||||
/**
|
||||
* Represents a Guild Channel (i.e. Text Channels and Voice Channels)
|
||||
@@ -62,67 +48,57 @@ class GuildChannel extends Channel {
|
||||
/**
|
||||
* Checks if this channel has the same type, topic, position, name, overwrites and ID as another channel.
|
||||
* In most cases, a simple `channel.id === channel2.id` will do, and is much faster too.
|
||||
* @param {GuildChannel} channel the channel to compare this channel to
|
||||
* @param {GuildChannel} channel The channel to compare this channel to
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(channel) {
|
||||
let base = channel &&
|
||||
let equal = channel &&
|
||||
this.type === channel.type &&
|
||||
this.topic === channel.topic &&
|
||||
this.position === channel.position &&
|
||||
this.name === channel.name &&
|
||||
this.id === channel.id;
|
||||
|
||||
if (base) {
|
||||
if (equal) {
|
||||
if (channel.permission_overwrites) {
|
||||
const thisIDSet = Array.from(this.permissionOverwrites.keys());
|
||||
const otherIDSet = channel.permission_overwrites.map(overwrite => overwrite.id);
|
||||
if (arraysEqual(thisIDSet, otherIDSet)) {
|
||||
base = true;
|
||||
} else {
|
||||
base = false;
|
||||
}
|
||||
equal = arraysEqual(thisIDSet, otherIDSet);
|
||||
} else {
|
||||
base = false;
|
||||
equal = false;
|
||||
}
|
||||
}
|
||||
|
||||
return base;
|
||||
return equal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overall set of permissions for a user in this channel, taking into account roles and permission
|
||||
* overwrites.
|
||||
* @param {GuildMemberResolvable} member the user that you want to obtain the overall permissions for
|
||||
* @param {GuildMemberResolvable} member The user that you want to obtain the overall permissions for
|
||||
* @returns {?EvaluatedPermissions}
|
||||
*/
|
||||
permissionsFor(member) {
|
||||
member = this.client.resolver.resolveGuildMember(this.guild, member);
|
||||
if (member) {
|
||||
if (this.guild.owner.id === member.id) {
|
||||
return new EvaluatedPermissions(member, Constants.ALL_PERMISSIONS);
|
||||
}
|
||||
if (this.guild.owner.id === member.id) return new EvaluatedPermissions(member, Constants.ALL_PERMISSIONS);
|
||||
|
||||
const roles = member.roles;
|
||||
let permissions = 0;
|
||||
const overwrites = this.overwritesFor(member, true);
|
||||
|
||||
for (const role of roles.values()) {
|
||||
permissions |= role.permissions;
|
||||
}
|
||||
|
||||
for (const role of roles.values()) permissions |= role.permissions;
|
||||
for (const overwrite of overwrites.role.concat(overwrites.member)) {
|
||||
permissions &= ~overwrite.denyData;
|
||||
permissions |= overwrite.allowData;
|
||||
}
|
||||
|
||||
const admin = Boolean(permissions & (Constants.PermissionFlags.ADMINISTRATOR));
|
||||
if (admin) {
|
||||
permissions = Constants.ALL_PERMISSIONS;
|
||||
}
|
||||
if (admin) permissions = Constants.ALL_PERMISSIONS;
|
||||
|
||||
return new EvaluatedPermissions(member, permissions);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -165,9 +141,9 @@ class GuildChannel extends Channel {
|
||||
|
||||
/**
|
||||
* Overwrites the permissions for a user or role in this channel.
|
||||
* @param {Role|UserResolvable} userOrRole the user or role to update
|
||||
* @param {PermissionOverwriteOptions} options the configuration for the update
|
||||
* @returns {Promise<null, Error>}
|
||||
* @param {Role|UserResolvable} userOrRole The user or role to update
|
||||
* @param {PermissionOverwriteOptions} options The configuration for the update
|
||||
* @returns {Promise}
|
||||
* @example
|
||||
* // overwrite permissions for a message author
|
||||
* message.channel.overwritePermissions(message.author, {
|
||||
@@ -187,9 +163,7 @@ class GuildChannel extends Channel {
|
||||
} else {
|
||||
userOrRole = this.client.resolver.resolveUser(userOrRole);
|
||||
payload.type = 'member';
|
||||
if (!userOrRole) {
|
||||
return Promise.reject('supplied parameter was neither a user or a role');
|
||||
}
|
||||
if (!userOrRole) return Promise.reject(new TypeError('supplied parameter was neither a user or a role'));
|
||||
}
|
||||
|
||||
payload.id = userOrRole.id;
|
||||
@@ -220,7 +194,7 @@ class GuildChannel extends Channel {
|
||||
|
||||
/**
|
||||
* Set a new name for the Guild Channel
|
||||
* @param {string} name the new name for the guild channel
|
||||
* @param {string} name The new name for the guild channel
|
||||
* @returns {Promise<GuildChannel>}
|
||||
* @example
|
||||
* // set a new channel name
|
||||
@@ -234,7 +208,7 @@ class GuildChannel extends Channel {
|
||||
|
||||
/**
|
||||
* Set a new position for the Guild Channel
|
||||
* @param {number} position the new position for the guild channel
|
||||
* @param {number} position The new position for the guild channel
|
||||
* @returns {Promise<GuildChannel>}
|
||||
* @example
|
||||
* // set a new channel position
|
||||
@@ -248,7 +222,7 @@ class GuildChannel extends Channel {
|
||||
|
||||
/**
|
||||
* Set a new topic for the Guild Channel
|
||||
* @param {string} topic the new topic for the guild channel
|
||||
* @param {string} topic The new topic for the guild channel
|
||||
* @returns {Promise<GuildChannel>}
|
||||
* @example
|
||||
* // set a new channel topic
|
||||
@@ -288,8 +262,8 @@ class GuildChannel extends Channel {
|
||||
|
||||
/**
|
||||
* Create an invite to this Guild Channel
|
||||
* @param {InviteOptions} [options={}] the options to provide when creating the invite
|
||||
* @returns {Promise<Invite, Error>}
|
||||
* @param {InviteOptions} [options={}] The options for the invite
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
createInvite(options = {}) {
|
||||
return this.client.rest.methods.createChannelInvite(this, options);
|
||||
|
||||
@@ -23,9 +23,7 @@ class GuildMember {
|
||||
*/
|
||||
this.user = {};
|
||||
this._roles = [];
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -91,15 +89,11 @@ class GuildMember {
|
||||
const list = new Collection();
|
||||
const everyoneRole = this.guild.roles.get(this.guild.id);
|
||||
|
||||
if (everyoneRole) {
|
||||
list.set(everyoneRole.id, everyoneRole);
|
||||
}
|
||||
if (everyoneRole) list.set(everyoneRole.id, everyoneRole);
|
||||
|
||||
for (const roleID of this._roles) {
|
||||
const role = this.guild.roles.get(roleID);
|
||||
if (role) {
|
||||
list.set(role.id, role);
|
||||
}
|
||||
if (role) list.set(role.id, role);
|
||||
}
|
||||
|
||||
return list;
|
||||
@@ -143,8 +137,8 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Mute/unmute a user
|
||||
* @param {boolean} mute whether or not the member should be muted
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {boolean} mute Whether or not the member should be muted
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setMute(mute) {
|
||||
return this.edit({ mute });
|
||||
@@ -152,8 +146,8 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Deafen/undeafen a user
|
||||
* @param {boolean} deaf whether or not the member should be deafened
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {boolean} deaf Whether or not the member should be deafened
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setDeaf(deaf) {
|
||||
return this.edit({ deaf });
|
||||
@@ -161,8 +155,8 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Moves the Guild Member to the given channel.
|
||||
* @param {ChannelResolvable} channel the channel to move the member to
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {ChannelResolvable} channel The channel to move the member to
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setVoiceChannel(channel) {
|
||||
return this.edit({ channel });
|
||||
@@ -170,8 +164,8 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Sets the Roles applied to the member.
|
||||
* @param {Collection<string, Role>|Array<Role>} roles the roles to apply
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {Collection<string, Role>|Role[]} roles The roles to apply
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setRoles(roles) {
|
||||
return this.edit({ roles });
|
||||
@@ -179,8 +173,8 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Set the nickname for the Guild Member
|
||||
* @param {string} nick the nickname for the Guild Member
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {string} nick The nickname for the Guild Member
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
setNickname(nick) {
|
||||
return this.edit({ nick });
|
||||
@@ -188,15 +182,15 @@ class GuildMember {
|
||||
|
||||
/**
|
||||
* Edit a Guild Member
|
||||
* @param {GuildmemberEditData} data the data to edit the member with
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @param {GuildmemberEditData} data The data to edit the member with
|
||||
* @returns {Promise<GuildMember>}
|
||||
*/
|
||||
edit(data) {
|
||||
return this.client.rest.methods.updateGuildMember(this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes any DM's with this Guild Member
|
||||
* Deletes any DMs with this Guild Member
|
||||
* @returns {Promise<DMChannel>}
|
||||
*/
|
||||
deleteDM() {
|
||||
@@ -215,7 +209,7 @@ class GuildMember {
|
||||
* Ban this Guild Member
|
||||
* @param {number} [deleteDays=0] The amount of days worth of messages from this member that should
|
||||
* also be deleted. Between `0` and `7`.
|
||||
* @returns {Promise<GuildMember, Error>}
|
||||
* @returns {Promise<GuildMember>}
|
||||
* @example
|
||||
* // ban a guild member
|
||||
* guildMember.ban(7);
|
||||
|
||||
@@ -98,7 +98,7 @@ class Invite {
|
||||
|
||||
/**
|
||||
* Deletes this invite
|
||||
* @returns {Promise<Invite, Error>}
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
delete() {
|
||||
return this.client.rest.methods.deleteInvite(this);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Collection = require('../util/Collection');
|
||||
const Attachment = require('./MessageAttachment');
|
||||
const Embed = require('./MessageEmbed');
|
||||
const Collection = require('../util/Collection');
|
||||
|
||||
/**
|
||||
* Represents a Message on Discord
|
||||
*/
|
||||
@@ -26,9 +27,7 @@ class Message {
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = client;
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -69,7 +68,7 @@ class Message {
|
||||
this.nonce = data.nonce;
|
||||
/**
|
||||
* A list of embeds in the message - e.g. YouTube Player
|
||||
* @type {Array<Embed>}
|
||||
* @type {Embed[]}
|
||||
*/
|
||||
this.embeds = data.embeds.map(e => new Embed(this, e));
|
||||
/**
|
||||
@@ -77,17 +76,15 @@ class Message {
|
||||
* @type {Collection<string, MessageAttachment>}
|
||||
*/
|
||||
this.attachments = new Collection();
|
||||
for (const attachment of data.attachments) {
|
||||
this.attachments.set(attachment.id, new Attachment(this, attachment));
|
||||
}
|
||||
for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment));
|
||||
/**
|
||||
* An object containing a further users, roles or channels collections
|
||||
* @type {Object}
|
||||
* @property {Collection<string, User>} mentions.users Mentioned users, maps their ID to the user object.
|
||||
* @property {Collection<string, Role>} mentions.roles Mentioned roles, maps their ID to the role object.
|
||||
* @property {Collection<string, GuildChannel>}
|
||||
* mentions.channels Mentioned channels, maps their ID to the channel object.
|
||||
* @property {boolean} mentions.everyone whether or not @everyone was mentioned.
|
||||
* @property {Collection<string, GuildChannel>} mentions.channels Mentioned channels,
|
||||
* maps their ID to the channel object.
|
||||
* @property {boolean} mentions.everyone Whether or not @everyone was mentioned.
|
||||
*/
|
||||
this.mentions = {
|
||||
users: new Collection(),
|
||||
@@ -114,9 +111,7 @@ class Message {
|
||||
if (data.mention_roles) {
|
||||
for (const mention of data.mention_roles) {
|
||||
const role = this.channel.guild.roles.get(mention);
|
||||
if (role) {
|
||||
this.mentions.roles.set(role.id, role);
|
||||
}
|
||||
if (role) this.mentions.roles.set(role.id, role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,9 +119,7 @@ class Message {
|
||||
const channMentionsRaw = data.content.match(/<#([0-9]{14,20})>/g) || [];
|
||||
for (const raw of channMentionsRaw) {
|
||||
const chan = this.channel.guild.channels.get(raw.match(/([0-9]{14,20})/g)[0]);
|
||||
if (chan) {
|
||||
this.mentions.channels.set(chan.id, chan);
|
||||
}
|
||||
if (chan) this.mentions.channels.set(chan.id, chan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +128,7 @@ class Message {
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.system = false;
|
||||
if (data.type === 6) {
|
||||
this.system = true;
|
||||
}
|
||||
if (data.type === 6) this.system = true;
|
||||
}
|
||||
/**
|
||||
* When the message was sent
|
||||
@@ -158,31 +149,17 @@ class Message {
|
||||
patch(data) { // eslint-disable-line complexity
|
||||
if (data.author) {
|
||||
this.author = this.client.users.get(data.author.id);
|
||||
if (this.guild) {
|
||||
this.member = this.guild.member(this.author);
|
||||
}
|
||||
}
|
||||
if (data.content) {
|
||||
this.content = data.content;
|
||||
}
|
||||
if (data.timestamp) {
|
||||
this._timestamp = new Date(data.timestamp).getTime();
|
||||
if (this.guild) this.member = this.guild.member(this.author);
|
||||
}
|
||||
if (data.content) this.content = data.content;
|
||||
if (data.timestamp) this._timestamp = new Date(data.timestamp).getTime();
|
||||
if (data.edited_timestamp) {
|
||||
this._editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null;
|
||||
}
|
||||
if ('tts' in data) {
|
||||
this.tts = data.tts;
|
||||
}
|
||||
if ('mention_everyone' in data) {
|
||||
this.mentions.everyone = data.mention_everyone;
|
||||
}
|
||||
if (data.nonce) {
|
||||
this.nonce = data.nonce;
|
||||
}
|
||||
if (data.embeds) {
|
||||
this.embeds = data.embeds.map(e => new Embed(this, e));
|
||||
}
|
||||
if ('tts' in data) this.tts = data.tts;
|
||||
if ('mention_everyone' in data) this.mentions.everyone = data.mention_everyone;
|
||||
if (data.nonce) this.nonce = data.nonce;
|
||||
if (data.embeds) this.embeds = data.embeds.map(e => new Embed(this, e));
|
||||
if (data.type > -1) {
|
||||
this.system = false;
|
||||
if (data.type === 6) {
|
||||
@@ -214,9 +191,7 @@ class Message {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.id) {
|
||||
this.id = data.id;
|
||||
}
|
||||
if (data.id) this.id = data.id;
|
||||
if (this.channel.guild && data.content) {
|
||||
const channMentionsRaw = data.content.match(/<#([0-9]{14,20})>/g) || [];
|
||||
for (const raw of channMentionsRaw) {
|
||||
@@ -237,14 +212,11 @@ class Message {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(message, rawData) {
|
||||
if (!message) return false;
|
||||
const embedUpdate = !message.author && !message.attachments;
|
||||
if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length;
|
||||
|
||||
if (embedUpdate) {
|
||||
const base = this.id === message.id &&
|
||||
this.embeds.length === message.embeds.length;
|
||||
return base;
|
||||
}
|
||||
let base = this.id === message.id &&
|
||||
let equal = this.id === message.id &&
|
||||
this.author.id === message.author.id &&
|
||||
this.content === message.content &&
|
||||
this.tts === message.tts &&
|
||||
@@ -252,19 +224,19 @@ class Message {
|
||||
this.embeds.length === message.embeds.length &&
|
||||
this.attachments.length === message.attachments.length;
|
||||
|
||||
if (base && rawData) {
|
||||
base = this.mentions.everyone === message.mentions.everyone &&
|
||||
if (equal && rawData) {
|
||||
equal = this.mentions.everyone === message.mentions.everyone &&
|
||||
this._timestamp === new Date(rawData.timestamp).getTime() &&
|
||||
this._editedTimestamp === new Date(rawData.edited_timestamp).getTime();
|
||||
}
|
||||
|
||||
return base;
|
||||
return equal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the message
|
||||
* @param {number} [timeout=0] How long to wait to delete the message in milliseconds
|
||||
* @returns {Promise<Message, Error>}
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // delete a message
|
||||
* message.delete()
|
||||
@@ -283,8 +255,8 @@ class Message {
|
||||
|
||||
/**
|
||||
* Edit the content of a message
|
||||
* @param {string} content the new content of a message
|
||||
* @returns {Promise<Message, Error>}
|
||||
* @param {string} content The new content for the message
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // update the content of a message
|
||||
* message.edit('This is my new content!')
|
||||
@@ -297,9 +269,9 @@ class Message {
|
||||
|
||||
/**
|
||||
* Reply to a message
|
||||
* @param {string} content the content of the message
|
||||
* @param {MessageOptions} [options = {}] the options to provide
|
||||
* @returns {Promise<Message, Error>}
|
||||
* @param {string} content The content for the message
|
||||
* @param {MessageOptions} [options = {}] The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // reply to a message
|
||||
* message.reply('Hey, I'm a reply!')
|
||||
@@ -313,7 +285,7 @@ class Message {
|
||||
|
||||
/**
|
||||
* Pins this message to the channel's pinned messages
|
||||
* @returns {Promise<Message, Error>}
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
pin() {
|
||||
return this.client.rest.methods.pinMessage(this);
|
||||
@@ -321,7 +293,7 @@ class Message {
|
||||
|
||||
/**
|
||||
* Unpins this message from the channel's pinned messages
|
||||
* @returns {Promise<Message, Error>}
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
unpin() {
|
||||
return this.client.rest.methods.unpinMessage(this);
|
||||
|
||||
@@ -1,3 +1,59 @@
|
||||
/**
|
||||
* Represents an embed in an image - e.g. preview of image
|
||||
*/
|
||||
class MessageEmbed {
|
||||
constructor(message, data) {
|
||||
/**
|
||||
* The message this embed is part of
|
||||
* @type {Message}
|
||||
*/
|
||||
this.message = message;
|
||||
/**
|
||||
* The client that instantiated this embed
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = message.client;
|
||||
this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
/**
|
||||
* The title of this embed, if there is one
|
||||
* @type {?string}
|
||||
*/
|
||||
this.title = data.title;
|
||||
/**
|
||||
* The type of this embed
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = data.type;
|
||||
/**
|
||||
* The description of this embed, if there is one
|
||||
* @type {?string}
|
||||
*/
|
||||
this.description = data.description;
|
||||
/**
|
||||
* The URL of this embed
|
||||
* @type {string}
|
||||
*/
|
||||
this.url = data.url;
|
||||
if (data.thumbnail) {
|
||||
/**
|
||||
* The thumbnail of this embed, if there is one
|
||||
* @type {MessageEmbedThumbnail}
|
||||
*/
|
||||
this.thumbnail = new MessageEmbedThumbnail(this, data.thumbnail);
|
||||
}
|
||||
if (data.provider) {
|
||||
/**
|
||||
* The provider of this embed, if there is one
|
||||
* @type {MessageEmbedProvider}
|
||||
*/
|
||||
this.provider = new MessageEmbedProvider(this, data.provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a thumbnail for a Message embed
|
||||
*/
|
||||
@@ -62,60 +118,4 @@ class MessageEmbedProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an embed in an image - e.g. preview of image
|
||||
*/
|
||||
class MessageEmbed {
|
||||
constructor(message, data) {
|
||||
/**
|
||||
* The message this embed is part of
|
||||
* @type {Message}
|
||||
*/
|
||||
this.message = message;
|
||||
/**
|
||||
* The client that instantiated this embed
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = message.client;
|
||||
this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
/**
|
||||
* The title of this embed, if there is one
|
||||
* @type {?string}
|
||||
*/
|
||||
this.title = data.title;
|
||||
/**
|
||||
* The type of this embed
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = data.type;
|
||||
/**
|
||||
* The description of this embed, if there is one
|
||||
* @type {?string}
|
||||
*/
|
||||
this.description = data.description;
|
||||
/**
|
||||
* The URL of this embed
|
||||
* @type {string}
|
||||
*/
|
||||
this.url = data.url;
|
||||
if (data.thumbnail) {
|
||||
/**
|
||||
* The thumbnail of this embed, if there is one
|
||||
* @type {MessageEmbedThumbnail}
|
||||
*/
|
||||
this.thumbnail = new MessageEmbedThumbnail(this, data.thumbnail);
|
||||
}
|
||||
if (data.provider) {
|
||||
/**
|
||||
* The provider of this embed, if there is one
|
||||
* @type {MessageEmbedProvider}
|
||||
*/
|
||||
this.provider = new MessageEmbedProvider(this, data.provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageEmbed;
|
||||
|
||||
@@ -8,9 +8,7 @@ class PermissionOverwrites {
|
||||
* @type {GuildChannel}
|
||||
*/
|
||||
this.channel = guildChannel;
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -30,7 +28,7 @@ class PermissionOverwrites {
|
||||
|
||||
/**
|
||||
* Delete this Permission Overwrite.
|
||||
* @returns {Promise<PermissionOverwrites, Error>}
|
||||
* @returns {Promise<PermissionOverwrites>}
|
||||
*/
|
||||
delete() {
|
||||
return this.channel.client.rest.methods.deletePermissionOverwrites(this);
|
||||
|
||||
@@ -15,22 +15,18 @@ class Role {
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = guild.client;
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
equals(role) {
|
||||
return (
|
||||
role &&
|
||||
return role &&
|
||||
this.id === role.id &&
|
||||
this.name === role.name &&
|
||||
this.color === role.color &&
|
||||
this.hoist === role.hoist &&
|
||||
this.position === role.position &&
|
||||
this.permissions === role.permissions &&
|
||||
this.managed === role.managed
|
||||
);
|
||||
this.managed === role.managed;
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -73,7 +69,7 @@ class Role {
|
||||
|
||||
/**
|
||||
* Deletes the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // delete a role
|
||||
* role.delete()
|
||||
@@ -86,8 +82,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Edits the role
|
||||
* @param {RoleData} data the new data for the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {RoleData} data The new data for the role
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // edit a role
|
||||
* role.edit({name: 'new role'})
|
||||
@@ -100,8 +96,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Set a new name for the role
|
||||
* @param {string} name the new name of the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {string} name The new name of the role
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // set the name of the role
|
||||
* role.setName('new role')
|
||||
@@ -114,8 +110,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Set a new color for the role
|
||||
* @param {number|string} color the new color for the role, either a hex string or a base 10 number
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {number|string} color The new color for the role, either a hex string or a base 10 number
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // set the color of a role
|
||||
* role.setColor('#FF0000')
|
||||
@@ -128,8 +124,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Set whether or not the role should be hoisted
|
||||
* @param {boolean} hoist whether or not to hoist the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {boolean} hoist Whether or not to hoist the role
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // set the hoist of the role
|
||||
* role.setHoist(true)
|
||||
@@ -142,8 +138,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Set the position of the role
|
||||
* @param {number} position the position of the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {number} position The position of the role
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // set the position of the role
|
||||
* role.setPosition(1)
|
||||
@@ -156,8 +152,8 @@ class Role {
|
||||
|
||||
/**
|
||||
* Set the permissions of the role
|
||||
* @param {Array<string>} permissions the permissions of the role
|
||||
* @returns {Promise<Role, Error>}
|
||||
* @param {string[]} permissions The permissions of the role
|
||||
* @returns {Promise<Role>}
|
||||
* @example
|
||||
* // set the permissions of the role
|
||||
* role.setPermissions(['KICK_MEMBERS', 'BAN_MEMBERS'])
|
||||
@@ -180,14 +176,13 @@ class Role {
|
||||
for (const permissionName in Constants.PermissionFlags) {
|
||||
serializedPermissions[permissionName] = this.hasPermission(permissionName);
|
||||
}
|
||||
|
||||
return serializedPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the role includes the given permission
|
||||
* @param {string} permission the name of the permission to test
|
||||
* @param {boolean} [explicit=false] whether or not the inclusion of the permission is explicit
|
||||
* @param {PermissionResolvable} permission The name of the permission to test
|
||||
* @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
|
||||
* @returns {boolean}
|
||||
* @example
|
||||
* // see if a role can ban a member
|
||||
@@ -198,20 +193,8 @@ class Role {
|
||||
* }
|
||||
*/
|
||||
hasPermission(permission, explicit = false) {
|
||||
if (typeof permission === 'string') {
|
||||
permission = Constants.PermissionFlags[permission];
|
||||
}
|
||||
|
||||
if (!permission) {
|
||||
throw Constants.Errors.NOT_A_PERMISSION;
|
||||
}
|
||||
|
||||
if (!explicit) {
|
||||
if ((this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
permission = this.client.resolver.resolvePermission(permission);
|
||||
if (!explicit && (this.permissions & Constants.PermissionFlags.ADMINISTRATOR) > 0) return true;
|
||||
return (this.permissions & permission) > 0;
|
||||
}
|
||||
|
||||
@@ -230,9 +213,7 @@ class Role {
|
||||
*/
|
||||
get hexColor() {
|
||||
let col = this.color.toString(16);
|
||||
while (col.length < 6) {
|
||||
col = `0${col}`;
|
||||
}
|
||||
while (col.length < 6) col = `0${col}`;
|
||||
return `#${col}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const Collection = require('../util/Collection');
|
||||
* @implements {TextBasedChannel}
|
||||
*/
|
||||
class TextChannel extends GuildChannel {
|
||||
|
||||
constructor(guild, data) {
|
||||
super(guild, data);
|
||||
this.messages = new Collection();
|
||||
|
||||
@@ -8,9 +8,7 @@ const Constants = require('../util/Constants');
|
||||
class User {
|
||||
constructor(client, data) {
|
||||
this.client = client;
|
||||
if (data) {
|
||||
this.setup(data);
|
||||
}
|
||||
if (data) this.setup(data);
|
||||
}
|
||||
|
||||
setup(data) {
|
||||
@@ -72,9 +70,7 @@ class User {
|
||||
* @readonly
|
||||
*/
|
||||
get avatarURL() {
|
||||
if (!this.avatar) {
|
||||
return null;
|
||||
}
|
||||
if (!this.avatar) return null;
|
||||
return Constants.Endpoints.avatar(this.id, this.avatar);
|
||||
}
|
||||
|
||||
@@ -89,28 +85,23 @@ class User {
|
||||
/**
|
||||
* Checks if the user is equal to another. It compares username, ID, discriminator, status and the game being played.
|
||||
* It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
|
||||
* @param {User} user the user to compare
|
||||
* @param {User} user The user to compare
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(user) {
|
||||
let base = user &&
|
||||
let equal = user &&
|
||||
this.username === user.username &&
|
||||
this.id === user.id &&
|
||||
this.discriminator === user.discriminator &&
|
||||
this.avatar === user.avatar &&
|
||||
this.bot === Boolean(user.bot);
|
||||
|
||||
if (base) {
|
||||
if (user.status) {
|
||||
base = this.status === user.status;
|
||||
}
|
||||
|
||||
if (user.game) {
|
||||
base = this.game === user.game;
|
||||
}
|
||||
if (equal) {
|
||||
if (user.status) equal = this.status === user.status;
|
||||
if (equal && user.game) equal = this.game === user.game;
|
||||
}
|
||||
|
||||
return base;
|
||||
return equal;
|
||||
}
|
||||
|
||||
sendMessage() {
|
||||
|
||||
@@ -32,7 +32,7 @@ class VoiceChannel extends GuildChannel {
|
||||
|
||||
/**
|
||||
* Sets the bitrate of the channel
|
||||
* @param {number} bitrate the new bitrate
|
||||
* @param {number} bitrate The new bitrate
|
||||
* @returns {Promise<VoiceChannel>}
|
||||
* @example
|
||||
* // set the bitrate of a voice channel
|
||||
@@ -46,7 +46,7 @@ class VoiceChannel extends GuildChannel {
|
||||
|
||||
/**
|
||||
* Attempts to join this Voice Channel
|
||||
* @returns {Promise<VoiceConnection, Error>}
|
||||
* @returns {Promise<VoiceConnection>}
|
||||
* @example
|
||||
* // join a voice channel
|
||||
* voiceChannel.join()
|
||||
@@ -64,12 +64,8 @@ class VoiceChannel extends GuildChannel {
|
||||
* voiceChannel.leave();
|
||||
*/
|
||||
leave() {
|
||||
const exists = this.client.voice.connections.get(this.guild.id);
|
||||
if (exists) {
|
||||
if (exists.channel.id === this.id) {
|
||||
exists.disconnect();
|
||||
}
|
||||
}
|
||||
const connection = this.client.voice.connections.get(this.guild.id);
|
||||
if (connection && connection.channel.id === this.id) connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,128 +1,13 @@
|
||||
const Collection = require('../../util/Collection');
|
||||
const Message = require('../Message');
|
||||
const path = require('path');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* A function that takes a Message object and a MessageCollector and returns a boolean.
|
||||
* ```js
|
||||
* function(message, collector) {
|
||||
* if (message.content.includes('discord')) {
|
||||
* return true; // passed the filter test
|
||||
* }
|
||||
* return false; // failed the filter test
|
||||
* }
|
||||
* ```
|
||||
* @typedef {function} CollectorFilterFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object containing options used to configure a MessageCollector. All properties are optional.
|
||||
* ```js
|
||||
* {
|
||||
* time: null, // time in milliseconds. If specified, the collector ends after this amount of time.
|
||||
* max: null, // the maximum amount of messages to handle before ending.
|
||||
* }
|
||||
* ```
|
||||
* @typedef {Object} CollectorOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Collects messages based on a specified filter, then emits them.
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class MessageCollector extends EventEmitter {
|
||||
constructor(channel, filter, options = {}) {
|
||||
super();
|
||||
/**
|
||||
* The channel this collector is operating on
|
||||
* @type {Channel}
|
||||
*/
|
||||
this.channel = channel;
|
||||
/**
|
||||
* A function used to filter messages that the collector collects.
|
||||
* @type {CollectorFilterFunction}
|
||||
*/
|
||||
this.filter = filter;
|
||||
/**
|
||||
* Options for the collecor.
|
||||
* @type {CollectorOptions}
|
||||
*/
|
||||
this.options = options;
|
||||
/**
|
||||
* Whether this collector has stopped collecting Messages.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.ended = false;
|
||||
this.listener = message => this.verify(message);
|
||||
this.channel.client.on('message', this.listener);
|
||||
/**
|
||||
* A collection of collected messages, mapped by message ID.
|
||||
* @type {Collection<string, Message>}
|
||||
*/
|
||||
this.collected = new Collection();
|
||||
if (options.time) {
|
||||
this.channel.client.setTimeout(() => this.stop('time'), options.time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a message against the filter and options
|
||||
* @private
|
||||
* @param {Message} message the message
|
||||
* @returns {boolean}
|
||||
*/
|
||||
verify(message) {
|
||||
if (this.channel ? this.channel.id !== message.channel.id : false) {
|
||||
return false;
|
||||
}
|
||||
if (this.filter(message, this)) {
|
||||
this.collected.set(message.id, message);
|
||||
/**
|
||||
* Emitted whenever the Collector receives a Message that passes the filter test.
|
||||
* @param {Message} message the received message
|
||||
* @param {MessageCollector} collector the collector the message passed through.
|
||||
* @event MessageCollector#message
|
||||
*/
|
||||
this.emit('message', message, this);
|
||||
if (this.options.max && this.collected.size === this.options.max) {
|
||||
this.stop('limit');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the collector and emits `end`.
|
||||
* @param {string} [reason='user'] an optional reason for stopping the collector.
|
||||
*/
|
||||
stop(reason = 'user') {
|
||||
if (this.ended) {
|
||||
return;
|
||||
}
|
||||
this.ended = true;
|
||||
this.channel.client.removeListener('message', this.listener);
|
||||
/**
|
||||
* Emitted when the Collector stops collecting.
|
||||
* @param {Collection<string, Message>} collection A collection of messages collected
|
||||
* during the lifetime of the Collector.
|
||||
* Mapped by the ID of the Messages.
|
||||
* @param {string} reason The reason for the end of the collector. If it ended because it reached the specified time
|
||||
* limit, this would be `time`. If you invoke `.stop()` without specifying a reason, this would be `user`. If it
|
||||
* ended because it reached its message limit, it will be `limit`.
|
||||
* @event MessageCollector#end
|
||||
*/
|
||||
this.emit('end', this.collected, reason);
|
||||
}
|
||||
}
|
||||
const Message = require('../Message');
|
||||
const Collection = require('../../util/Collection');
|
||||
|
||||
/**
|
||||
* Interface for classes that have text-channel-like features
|
||||
* @interface
|
||||
*/
|
||||
class TextBasedChannel {
|
||||
|
||||
constructor() {
|
||||
/**
|
||||
* A Collection containing the messages sent to this channel.
|
||||
@@ -133,19 +18,16 @@ class TextBasedChannel {
|
||||
|
||||
/**
|
||||
* Bulk delete a given Collection or Array of messages in one go. Returns the deleted messages after.
|
||||
* @param {Map<string, Message>|Array<Message>} messages the messages to delete
|
||||
* @param {Collection<string, Message>|Message[]} messages The messages to delete
|
||||
* @returns {Collection<string, Message>}
|
||||
*/
|
||||
bulkDelete(messages) {
|
||||
if (messages instanceof Map) {
|
||||
messages = messages.array();
|
||||
}
|
||||
if (!(messages instanceof Array)) {
|
||||
return Promise.reject('pass an array or map');
|
||||
}
|
||||
if (messages instanceof Collection) messages = messages.array();
|
||||
if (!(messages instanceof Array)) return Promise.reject(new TypeError('messages must be an array or collection'));
|
||||
const messageIDs = messages.map(m => m.id);
|
||||
return this.client.rest.methods.bulkDeleteMessages(this, messageIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options that can be passed into sendMessage or sendTTSMessage:
|
||||
* ```js
|
||||
@@ -156,10 +38,11 @@ class TextBasedChannel {
|
||||
* ```
|
||||
* @typedef {Object} MessageOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send a message to this channel
|
||||
* @param {string} content the content to send
|
||||
* @param {MessageOptions} [options={}] the options to provide
|
||||
* @param {string} content The content to send
|
||||
* @param {MessageOptions} [options={}] The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // send a message
|
||||
@@ -170,10 +53,11 @@ class TextBasedChannel {
|
||||
sendMessage(content, options = {}) {
|
||||
return this.client.rest.methods.sendMessage(this, content, options.tts, options.nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a text-to-speech message to this channel
|
||||
* @param {string} content the content to send
|
||||
* @param {MessageOptions} [options={}] the options to provide
|
||||
* @param {string} content The content to send
|
||||
* @param {MessageOptions} [options={}] The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // send a TTS message
|
||||
@@ -184,6 +68,7 @@ class TextBasedChannel {
|
||||
sendTTSMessage(content, options = {}) {
|
||||
return this.client.rest.methods.sendMessage(this, content, true, options.nonce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a file to this channel
|
||||
* @param {FileResolvable} attachment The file to send
|
||||
@@ -201,16 +86,15 @@ class TextBasedChannel {
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.resolver.resolveFile(attachment)
|
||||
.then(file => {
|
||||
this.client.resolver.resolveFile(attachment).then(file => {
|
||||
this.client.rest.methods.sendMessage(this, undefined, false, undefined, {
|
||||
file,
|
||||
name: fileName,
|
||||
}).then(resolve).catch(reject);
|
||||
})
|
||||
.catch(reject);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
|
||||
* `after` are mutually exclusive. All the parameters are optional.
|
||||
@@ -227,8 +111,8 @@ class TextBasedChannel {
|
||||
|
||||
/**
|
||||
* Gets the past messages sent in this channel. Resolves with a Collection mapping message ID's to Message objects.
|
||||
* @param {ChannelLogsQueryOptions} [options={}] the query parameters to pass in
|
||||
* @returns {Promise<Collection<string, Message>, Error>}
|
||||
* @param {ChannelLogsQueryOptions} [options={}] The query parameters to pass in
|
||||
* @returns {Promise<Collection<string, Message>>}
|
||||
* @example
|
||||
* // get messages
|
||||
* channel.fetchMessages({limit: 10})
|
||||
@@ -237,17 +121,15 @@ class TextBasedChannel {
|
||||
*/
|
||||
fetchMessages(options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.rest.methods.getChannelMessages(this, options)
|
||||
.then(data => {
|
||||
const messages = new Collection();
|
||||
for (const message of data) {
|
||||
const msg = new Message(this, message, this.client);
|
||||
messages.set(message.id, msg);
|
||||
this._cacheMessage(msg);
|
||||
}
|
||||
resolve(messages);
|
||||
})
|
||||
.catch(reject);
|
||||
this.client.rest.methods.getChannelMessages(this, options).then(data => {
|
||||
const messages = new Collection();
|
||||
for (const message of data) {
|
||||
const msg = new Message(this, message, this.client);
|
||||
messages.set(message.id, msg);
|
||||
this._cacheMessage(msg);
|
||||
}
|
||||
resolve(messages);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,7 +160,7 @@ class TextBasedChannel {
|
||||
* Stops the typing indicator in the channel.
|
||||
* The indicator will only stop if this is called as many times as startTyping().
|
||||
* <info>It can take a few seconds for the Client User to stop typing.</info>
|
||||
* @param {boolean} [force=false] whether or not to force the indicator to stop regardless of call count
|
||||
* @param {boolean} [force=false] Whether or not to reset the call count and force the indicator to stop
|
||||
* @example
|
||||
* // stop typing in a channel
|
||||
* channel.stopTyping();
|
||||
@@ -316,8 +198,8 @@ class TextBasedChannel {
|
||||
|
||||
/**
|
||||
* Creates a Message Collector
|
||||
* @param {CollectorFilterFunction} filter the filter to create the collector with
|
||||
* @param {CollectorOptions} [options={}] the options to pass to the collector
|
||||
* @param {CollectorFilterFunction} filter The filter to create the collector with
|
||||
* @param {CollectorOptions} [options={}] The options to pass to the collector
|
||||
* @returns {MessageCollector}
|
||||
* @example
|
||||
* // create a message collector
|
||||
@@ -329,8 +211,7 @@ class TextBasedChannel {
|
||||
* collector.on('end', collected => console.log(`Collected ${collected.size} items`));
|
||||
*/
|
||||
createCollector(filter, options = {}) {
|
||||
const collector = new MessageCollector(this, filter, options);
|
||||
return collector;
|
||||
return new MessageCollector(this, filter, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,8 +227,8 @@ class TextBasedChannel {
|
||||
/**
|
||||
* Similar to createCollector but in Promise form. Resolves with a Collection of messages that pass the specified
|
||||
* filter.
|
||||
* @param {CollectorFilterFunction} filter the filter function to use
|
||||
* @param {AwaitMessagesOptions} [options={}] optional options to pass to the internal collector
|
||||
* @param {CollectorFilterFunction} filter The filter function to use
|
||||
* @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
|
||||
* @returns {Promise<Collection<string, Message>>}
|
||||
* @example
|
||||
* // await !vote messages
|
||||
@@ -372,43 +253,140 @@ class TextBasedChannel {
|
||||
|
||||
_cacheMessage(message) {
|
||||
const maxSize = this.client.options.max_message_cache;
|
||||
if (maxSize === 0) {
|
||||
// saves on performance
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.messages.size >= maxSize) {
|
||||
this.messages.delete(this.messages.keys().next().value);
|
||||
}
|
||||
if (maxSize === 0) return null;
|
||||
if (this.messages.size >= maxSize) this.messages.delete(this.messages.keys().next().value);
|
||||
|
||||
this.messages.set(message.id, message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the pinned messages of this Channel and returns a Collection of them.
|
||||
* @returns {Promise<Collection<string, Message>, Error>}
|
||||
* @returns {Promise<Collection<string, Message>>}
|
||||
*/
|
||||
fetchPinnedMessages() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.rest.methods.getChannelPinnedMessages(this)
|
||||
.then(data => {
|
||||
const messages = new Collection();
|
||||
for (const message of data) {
|
||||
const msg = new Message(this, message, this.client);
|
||||
messages.set(message.id, msg);
|
||||
this._cacheMessage(msg);
|
||||
}
|
||||
resolve(messages);
|
||||
})
|
||||
.catch(reject);
|
||||
this.client.rest.methods.getChannelPinnedMessages(this).then(data => {
|
||||
const messages = new Collection();
|
||||
for (const message of data) {
|
||||
const msg = new Message(this, message, this.client);
|
||||
messages.set(message.id, msg);
|
||||
this._cacheMessage(msg);
|
||||
}
|
||||
resolve(messages);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function applyProp(structure, prop) {
|
||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop));
|
||||
/**
|
||||
* Collects messages based on a specified filter, then emits them.
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class MessageCollector extends EventEmitter {
|
||||
/**
|
||||
* A function that takes a Message object and a MessageCollector and returns a boolean.
|
||||
* ```js
|
||||
* function(message, collector) {
|
||||
* if (message.content.includes('discord')) {
|
||||
* return true; // passed the filter test
|
||||
* }
|
||||
* return false; // failed the filter test
|
||||
* }
|
||||
* ```
|
||||
* @typedef {function} CollectorFilterFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object containing options used to configure a MessageCollector. All properties are optional.
|
||||
* ```js
|
||||
* {
|
||||
* time: null, // time in milliseconds. If specified, the collector ends after this amount of time.
|
||||
* max: null, // the maximum amount of messages to handle before ending.
|
||||
* }
|
||||
* ```
|
||||
* @typedef {Object} CollectorOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Channel} channel The channel to collect messages in
|
||||
* @param {CollectorFilterFunction} filter The filter function
|
||||
* @param {CollectorOptions} [options] Options for the collector
|
||||
*/
|
||||
constructor(channel, filter, options = {}) {
|
||||
super();
|
||||
/**
|
||||
* The channel this collector is operating on
|
||||
* @type {Channel}
|
||||
*/
|
||||
this.channel = channel;
|
||||
/**
|
||||
* A function used to filter messages that the collector collects.
|
||||
* @type {CollectorFilterFunction}
|
||||
*/
|
||||
this.filter = filter;
|
||||
/**
|
||||
* Options for the collecor.
|
||||
* @type {CollectorOptions}
|
||||
*/
|
||||
this.options = options;
|
||||
/**
|
||||
* Whether this collector has stopped collecting Messages.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.ended = false;
|
||||
this.listener = message => this.verify(message);
|
||||
this.channel.client.on('message', this.listener);
|
||||
/**
|
||||
* A collection of collected messages, mapped by message ID.
|
||||
* @type {Collection<string, Message>}
|
||||
*/
|
||||
this.collected = new Collection();
|
||||
if (options.time) this.channel.client.setTimeout(() => this.stop('time'), options.time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a message against the filter and options
|
||||
* @private
|
||||
* @param {Message} message The message
|
||||
* @returns {boolean}
|
||||
*/
|
||||
verify(message) {
|
||||
if (this.channel ? this.channel.id !== message.channel.id : false) return false;
|
||||
if (this.filter(message, this)) {
|
||||
this.collected.set(message.id, message);
|
||||
/**
|
||||
* Emitted whenever the Collector receives a Message that passes the filter test.
|
||||
* @param {Message} message The received message
|
||||
* @param {MessageCollector} collector The collector the message passed through
|
||||
* @event MessageCollector#message
|
||||
*/
|
||||
this.emit('message', message, this);
|
||||
if (this.options.max && this.collected.size === this.options.max) this.stop('limit');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the collector and emits `end`.
|
||||
* @param {string} [reason='user'] An optional reason for stopping the collector
|
||||
*/
|
||||
stop(reason = 'user') {
|
||||
if (this.ended) return;
|
||||
this.ended = true;
|
||||
this.channel.client.removeListener('message', this.listener);
|
||||
/**
|
||||
* Emitted when the Collector stops collecting.
|
||||
* @param {Collection<string, Message>} collection A collection of messages collected
|
||||
* during the lifetime of the Collector, mapped by the ID of the Messages.
|
||||
* @param {string} reason The reason for the end of the collector. If it ended because it reached the specified time
|
||||
* limit, this would be `time`. If you invoke `.stop()` without specifying a reason, this would be `user`. If it
|
||||
* ended because it reached its message limit, it will be `limit`.
|
||||
* @event MessageCollector#end
|
||||
*/
|
||||
this.emit('end', this.collected, reason);
|
||||
}
|
||||
}
|
||||
|
||||
exports.applyToClass = (structure, full = false) => {
|
||||
@@ -425,7 +403,9 @@ exports.applyToClass = (structure, full = false) => {
|
||||
props.push('createCollector');
|
||||
props.push('awaitMessages');
|
||||
}
|
||||
for (const prop of props) {
|
||||
applyProp(structure, prop);
|
||||
}
|
||||
for (const prop of props) applyProp(structure, prop);
|
||||
};
|
||||
|
||||
function applyProp(structure, prop) {
|
||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop));
|
||||
}
|
||||
|
||||
14
src/util/ArraysEqual.js
Normal file
14
src/util/ArraysEqual.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a.length !== b.length) return false;
|
||||
|
||||
for (const itemInd in a) {
|
||||
const item = a[itemInd];
|
||||
const ind = b.indexOf(item);
|
||||
if (ind) {
|
||||
b.splice(ind, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return b.length === 0;
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
module.exports = function cloneObject(obj) {
|
||||
const cloned = Object.create(obj);
|
||||
Object.assign(cloned, obj);
|
||||
|
||||
return cloned;
|
||||
};
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
* @extends {Map}
|
||||
*/
|
||||
class Collection extends Map {
|
||||
|
||||
/**
|
||||
* Returns an ordered array of the values of this collection.
|
||||
* @returns {Array}
|
||||
* @returns {*[]}
|
||||
* @example
|
||||
* // identical to:
|
||||
* Array.from(collection.values());
|
||||
@@ -17,18 +16,19 @@ class Collection extends Map {
|
||||
|
||||
/**
|
||||
* Returns the first item in this collection.
|
||||
* @returns {Object}
|
||||
* @returns {*}
|
||||
* @example
|
||||
* // identical to:
|
||||
* Array.from(collection.values())[0];
|
||||
*/
|
||||
first() {
|
||||
return this.array()[0];
|
||||
return this.values().next().value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last item in this collection.
|
||||
* @returns {Object}
|
||||
* Returns the last item in this collection. This is a relatively slow operation,
|
||||
* since an array copy of the values must be made to find the last element.
|
||||
* @returns {*}
|
||||
*/
|
||||
last() {
|
||||
const arr = this.array();
|
||||
@@ -36,8 +36,9 @@ class Collection extends Map {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random item from this collection.
|
||||
* @returns {Object}
|
||||
* Returns a random item from this collection. This is a relatively slow operation,
|
||||
* since an array copy of the values must be made to find a random element.
|
||||
* @returns {*}
|
||||
*/
|
||||
random() {
|
||||
const arr = this.array();
|
||||
@@ -47,32 +48,21 @@ class Collection extends Map {
|
||||
/**
|
||||
* If the items in this collection have a delete method (e.g. messages), invoke
|
||||
* the delete method. Returns an array of promises
|
||||
* @returns {Array<Promise>}
|
||||
* @returns {Promise[]}
|
||||
*/
|
||||
deleteAll() {
|
||||
const returns = [];
|
||||
for (const item of this.values()) {
|
||||
if (item.delete) {
|
||||
returns.push(item.delete());
|
||||
}
|
||||
if (item.delete) returns.push(item.delete());
|
||||
}
|
||||
return returns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The length (size) of this collection.
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
get length() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of items where `item[key] === value` of the collection
|
||||
* @param {string} key the key to filter by
|
||||
* @param {*} value the expected value
|
||||
* @returns {Array<Object>}
|
||||
* @param {string} key The key to filter by
|
||||
* @param {*} value The expected value
|
||||
* @returns {*[]}
|
||||
* @example
|
||||
* collection.getAll('username', 'Bob');
|
||||
*/
|
||||
@@ -81,18 +71,16 @@ class Collection extends Map {
|
||||
if (typeof value === 'undefined') throw new Error('value must be specified');
|
||||
const results = [];
|
||||
for (const item of this.values()) {
|
||||
if (item[key] === value) {
|
||||
results.push(item);
|
||||
}
|
||||
if (item[key] === value) results.push(item);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single item where `item[key] === value`
|
||||
* @param {string} key the key to filter by
|
||||
* @param {*} value the expected value
|
||||
* @returns {Object}
|
||||
* @param {string} key The key to filter by
|
||||
* @param {*} value The expected value
|
||||
* @returns {*}
|
||||
* @example
|
||||
* collection.get('id', '123123...');
|
||||
*/
|
||||
@@ -100,17 +88,15 @@ class Collection extends Map {
|
||||
if (typeof key !== 'string') throw new TypeError('key must be a string');
|
||||
if (typeof value === 'undefined') throw new Error('value must be specified');
|
||||
for (const item of this.values()) {
|
||||
if (item[key] === value) {
|
||||
return item;
|
||||
}
|
||||
if (item[key] === value) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the collection has an item where `item[key] === value`
|
||||
* @param {string} key the key to filter by
|
||||
* @param {*} value the expected value
|
||||
* @param {string} key The key to filter by
|
||||
* @param {*} value The expected value
|
||||
* @returns {boolean}
|
||||
* @example
|
||||
* if (collection.exists('id', '123123...')) {
|
||||
@@ -121,32 +107,26 @@ class Collection extends Map {
|
||||
return Boolean(this.find(key, value));
|
||||
}
|
||||
|
||||
_arrayMethod(method, args) {
|
||||
return Array.prototype[method].apply(this.array(), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to
|
||||
* [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),
|
||||
* but returns a Collection instead of an Array.
|
||||
* @param {function} callback the callback used to filter
|
||||
* @param {Object} [thisArg] value to set as this when filtering
|
||||
* @param {function} callback Function used to filter (should return a boolean)
|
||||
* @param {Object} [thisArg] Value to set as this when filtering
|
||||
* @returns {Collection}
|
||||
*/
|
||||
filter(...args) {
|
||||
const newArray = this.array().filter(...args);
|
||||
const collection = new Collection();
|
||||
for (const item of newArray) {
|
||||
collection.set(item.id, item);
|
||||
}
|
||||
for (const item of newArray) collection.set(item.id, item);
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functionally identical shortcut to `collection.array().map(...)`.
|
||||
* @param {function} callback Function that produces an element of the new Array, taking three arguments.
|
||||
* @param {function} callback Function that produces an element of the new Array, taking three arguments
|
||||
* @param {*} [thisArg] Optional. Value to use as this when executing callback.
|
||||
* @returns {array}
|
||||
* @returns {*[]}
|
||||
*/
|
||||
map(...args) {
|
||||
return this.array().map(...args);
|
||||
|
||||
@@ -67,7 +67,7 @@ exports.Errors = {
|
||||
NO_BOT_ACCOUNT: new Error('you should ideally be using a bot account!'),
|
||||
BAD_WS_MESSAGE: new Error('a bad message was received from the websocket - bad compression or not json'),
|
||||
TOOK_TOO_LONG: new Error('something took too long to do'),
|
||||
NOT_A_PERMISSION: new Error('that is not a valid permission number'),
|
||||
NOT_A_PERMISSION: new Error('that is not a valid permission string or number'),
|
||||
INVALID_RATE_LIMIT_METHOD: new Error('unknown rate limiting method'),
|
||||
BAD_LOGIN: new Error('incorrect login details were provided'),
|
||||
};
|
||||
@@ -235,10 +235,7 @@ const PermissionFlags = exports.PermissionFlags = {
|
||||
};
|
||||
|
||||
let _ALL_PERMISSIONS = 0;
|
||||
|
||||
for (const key in PermissionFlags) {
|
||||
_ALL_PERMISSIONS |= PermissionFlags[key];
|
||||
}
|
||||
for (const key in PermissionFlags) _ALL_PERMISSIONS |= PermissionFlags[key];
|
||||
|
||||
exports.ALL_PERMISSIONS = _ALL_PERMISSIONS;
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
module.exports = function merge(def, given) {
|
||||
if (!given) {
|
||||
return def;
|
||||
}
|
||||
|
||||
given = given || {};
|
||||
|
||||
if (!given) return def;
|
||||
for (const key in def) {
|
||||
if (!{}.hasOwnProperty.call(given, key)) {
|
||||
given[key] = def[key];
|
||||
|
||||
Reference in New Issue
Block a user