Initial Commit
This commit is contained in:
547
node_modules/discord.js/src/client/Client.js
generated
vendored
Normal file
547
node_modules/discord.js/src/client/Client.js
generated
vendored
Normal file
@@ -0,0 +1,547 @@
|
||||
const EventEmitter = require('events');
|
||||
const Constants = require('../util/Constants');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const Util = require('../util/Util');
|
||||
const RESTManager = require('./rest/RESTManager');
|
||||
const ClientDataManager = require('./ClientDataManager');
|
||||
const ClientManager = require('./ClientManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const ClientVoiceManager = require('./voice/ClientVoiceManager');
|
||||
const WebSocketManager = require('./websocket/WebSocketManager');
|
||||
const ActionsManager = require('./actions/ActionsManager');
|
||||
const Collection = require('../util/Collection');
|
||||
const Presence = require('../structures/Presence').Presence;
|
||||
const ShardClientUtil = require('../sharding/ShardClientUtil');
|
||||
const VoiceBroadcast = require('./voice/VoiceBroadcast');
|
||||
|
||||
/**
|
||||
* The main hub for interacting with the Discord API, and the starting point for any bot.
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class Client extends EventEmitter {
|
||||
/**
|
||||
* @param {ClientOptions} [options] Options for the client
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
// Obtain shard details from environment
|
||||
if (!options.shardId && 'SHARD_ID' in process.env) options.shardId = Number(process.env.SHARD_ID);
|
||||
if (!options.shardCount && 'SHARD_COUNT' in process.env) options.shardCount = Number(process.env.SHARD_COUNT);
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
|
||||
this._validateOptions();
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
* @type {RESTManager}
|
||||
* @private
|
||||
*/
|
||||
this.rest = new RESTManager(this);
|
||||
|
||||
/**
|
||||
* The data manager of the client
|
||||
* @type {ClientDataManager}
|
||||
* @private
|
||||
*/
|
||||
this.dataManager = new ClientDataManager(this);
|
||||
|
||||
/**
|
||||
* The manager of the client
|
||||
* @type {ClientManager}
|
||||
* @private
|
||||
*/
|
||||
this.manager = new ClientManager(this);
|
||||
|
||||
/**
|
||||
* The WebSocket manager of the client
|
||||
* @type {WebSocketManager}
|
||||
* @private
|
||||
*/
|
||||
this.ws = new WebSocketManager(this);
|
||||
|
||||
/**
|
||||
* The data resolver of the client
|
||||
* @type {ClientDataResolver}
|
||||
* @private
|
||||
*/
|
||||
this.resolver = new ClientDataResolver(this);
|
||||
|
||||
/**
|
||||
* The action manager of the client
|
||||
* @type {ActionsManager}
|
||||
* @private
|
||||
*/
|
||||
this.actions = new ActionsManager(this);
|
||||
|
||||
/**
|
||||
* The voice manager of the client (`null` in browsers)
|
||||
* @type {?ClientVoiceManager}
|
||||
* @private
|
||||
*/
|
||||
this.voice = !this.browser ? new ClientVoiceManager(this) : null;
|
||||
|
||||
/**
|
||||
* The shard helpers for the client
|
||||
* (only if the process was spawned as a child, such as from a {@link ShardingManager})
|
||||
* @type {?ShardClientUtil}
|
||||
*/
|
||||
this.shard = process.send ? ShardClientUtil.singleton(this) : null;
|
||||
|
||||
/**
|
||||
* All of the {@link User} objects that have been cached at any point, mapped by their IDs
|
||||
* @type {Collection<Snowflake, User>}
|
||||
*/
|
||||
this.users = new Collection();
|
||||
|
||||
/**
|
||||
* All of the guilds the client is currently handling, mapped by their IDs -
|
||||
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
|
||||
* @type {Collection<Snowflake, Guild>}
|
||||
*/
|
||||
this.guilds = new Collection();
|
||||
|
||||
/**
|
||||
* All of the {@link Channel}s that the client is currently handling, mapped by their IDs -
|
||||
* as long as sharding isn't being used, this will be *every* channel in *every* guild, and all DM channels
|
||||
* @type {Collection<Snowflake, Channel>}
|
||||
*/
|
||||
this.channels = new Collection();
|
||||
|
||||
/**
|
||||
* Presences that have been received for the client user's friends, mapped by user IDs
|
||||
* <warn>This is only filled when using a user account.</warn>
|
||||
* @type {Collection<Snowflake, Presence>}
|
||||
*/
|
||||
this.presences = new Collection();
|
||||
|
||||
Object.defineProperty(this, 'token', { writable: true });
|
||||
if (!this.token && 'CLIENT_TOKEN' in process.env) {
|
||||
/**
|
||||
* Authorization token for the logged in user/bot
|
||||
* <warn>This should be kept private at all times.</warn>
|
||||
* @type {?string}
|
||||
*/
|
||||
this.token = process.env.CLIENT_TOKEN;
|
||||
} else {
|
||||
this.token = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* User that the client is logged in as
|
||||
* @type {?ClientUser}
|
||||
*/
|
||||
this.user = null;
|
||||
|
||||
/**
|
||||
* Time at which the client was last regarded as being in the `READY` state
|
||||
* (each time the client disconnects and successfully reconnects, this will be overwritten)
|
||||
* @type {?Date}
|
||||
*/
|
||||
this.readyAt = null;
|
||||
|
||||
/**
|
||||
* Active voice broadcasts that have been created
|
||||
* @type {VoiceBroadcast[]}
|
||||
*/
|
||||
this.broadcasts = [];
|
||||
|
||||
/**
|
||||
* Previous heartbeat pings of the websocket (most recent first, limited to three elements)
|
||||
* @type {number[]}
|
||||
*/
|
||||
this.pings = [];
|
||||
|
||||
/**
|
||||
* Timeouts set by {@link Client#setTimeout} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._timeouts = new Set();
|
||||
|
||||
/**
|
||||
* Intervals set by {@link Client#setInterval} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._intervals = new Set();
|
||||
|
||||
if (this.options.messageSweepInterval > 0) {
|
||||
this.setInterval(this.sweepMessages.bind(this), this.options.messageSweepInterval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of the latest ping's start time
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
get _pingTimestamp() {
|
||||
return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current status of the client's connection to Discord
|
||||
* @type {?number}
|
||||
* @readonly
|
||||
*/
|
||||
get status() {
|
||||
return this.ws.connection.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* How long it has been since the client last entered the `READY` state in milliseconds
|
||||
* @type {?number}
|
||||
* @readonly
|
||||
*/
|
||||
get uptime() {
|
||||
return this.readyAt ? Date.now() - this.readyAt : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Average heartbeat ping of the websocket, obtained by averaging the {@link Client#pings} property
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get ping() {
|
||||
return this.pings.reduce((prev, p) => prev + p, 0) / this.pings.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* All active voice connections that have been established, mapped by guild ID
|
||||
* @type {Collection<Snowflake, VoiceConnection>}
|
||||
* @readonly
|
||||
*/
|
||||
get voiceConnections() {
|
||||
if (this.browser) return new Collection();
|
||||
return this.voice.connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* All custom emojis that the client has access to, mapped by their IDs
|
||||
* @type {Collection<Snowflake, Emoji>}
|
||||
* @readonly
|
||||
*/
|
||||
get emojis() {
|
||||
const emojis = new Collection();
|
||||
for (const guild of this.guilds.values()) {
|
||||
for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji);
|
||||
}
|
||||
return emojis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of the time the client was last `READY` at
|
||||
* @type {?number}
|
||||
* @readonly
|
||||
*/
|
||||
get readyTimestamp() {
|
||||
return this.readyAt ? this.readyAt.getTime() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client is in a browser environment
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get browser() {
|
||||
return typeof window !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a voice broadcast.
|
||||
* @returns {VoiceBroadcast}
|
||||
*/
|
||||
createVoiceBroadcast() {
|
||||
const broadcast = new VoiceBroadcast(this);
|
||||
this.broadcasts.push(broadcast);
|
||||
return broadcast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the client in, establishing a websocket connection to Discord.
|
||||
* <info>Both bot and regular user accounts are supported, but it is highly recommended to use a bot account whenever
|
||||
* possible. User accounts are subject to harsher ratelimits and other restrictions that don't apply to bot accounts.
|
||||
* Bot accounts also have access to many features that user accounts cannot utilise. User accounts that are found to
|
||||
* be abusing/overusing the API will be banned, locking you out of Discord entirely.</info>
|
||||
* @param {string} token Token of the account to log in with
|
||||
* @returns {Promise<string>} Token of the account used
|
||||
* @example
|
||||
* client.login('my token');
|
||||
*/
|
||||
login(token) {
|
||||
return this.rest.methods.login(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs out, terminates the connection to Discord, and destroys the client.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
destroy() {
|
||||
for (const t of this._timeouts) clearTimeout(t);
|
||||
for (const i of this._intervals) clearInterval(i);
|
||||
this._timeouts.clear();
|
||||
this._intervals.clear();
|
||||
return this.manager.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a sync of guild data with Discord.
|
||||
* <info>This can be done automatically every 30 seconds by enabling {@link ClientOptions#sync}.</info>
|
||||
* <warn>This is only available when using a user account.</warn>
|
||||
* @param {Guild[]|Collection<Snowflake, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
|
||||
*/
|
||||
syncGuilds(guilds = this.guilds) {
|
||||
if (this.user.bot) return;
|
||||
this.ws.send({
|
||||
op: 12,
|
||||
d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a user from Discord, or the user cache if it's already available.
|
||||
* <warn>This is only available when using a bot account.</warn>
|
||||
* @param {Snowflake} id ID of the user
|
||||
* @param {boolean} [cache=true] Whether to cache the new user object if it isn't already
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
fetchUser(id, cache = true) {
|
||||
if (this.users.has(id)) return Promise.resolve(this.users.get(id));
|
||||
return this.rest.methods.getUser(id, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an invite from Discord.
|
||||
* @param {InviteResolvable} invite Invite code or URL
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
fetchInvite(invite) {
|
||||
const code = this.resolver.resolveInviteCode(invite);
|
||||
return this.rest.methods.getInvite(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a webhook from Discord.
|
||||
* @param {Snowflake} id ID of the webhook
|
||||
* @param {string} [token] Token for the webhook
|
||||
* @returns {Promise<Webhook>}
|
||||
*/
|
||||
fetchWebhook(id, token) {
|
||||
return this.rest.methods.getWebhook(id, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the available voice regions from Discord.
|
||||
* @returns {Collection<string, VoiceRegion>}
|
||||
*/
|
||||
fetchVoiceRegions() {
|
||||
return this.rest.methods.fetchVoiceRegions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeps all text-based channels' messages and removes the ones older than the max message lifetime.
|
||||
* If the message has been edited, the time of the edit is used rather than the time of the original message.
|
||||
* @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds)
|
||||
* will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime}
|
||||
* @returns {number} Amount of messages that were removed from the caches,
|
||||
* or -1 if the message cache lifetime is unlimited
|
||||
*/
|
||||
sweepMessages(lifetime = this.options.messageCacheLifetime) {
|
||||
if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('The lifetime must be a number.');
|
||||
if (lifetime <= 0) {
|
||||
this.emit('debug', 'Didn\'t sweep messages - lifetime is unlimited');
|
||||
return -1;
|
||||
}
|
||||
|
||||
const lifetimeMs = lifetime * 1000;
|
||||
const now = Date.now();
|
||||
let channels = 0;
|
||||
let messages = 0;
|
||||
|
||||
for (const channel of this.channels.values()) {
|
||||
if (!channel.messages) continue;
|
||||
channels++;
|
||||
|
||||
for (const message of channel.messages.values()) {
|
||||
if (now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs) {
|
||||
channel.messages.delete(message.id);
|
||||
messages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('debug', `Swept ${messages} messages older than ${lifetime} seconds in ${channels} text-based channels`);
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the OAuth Application of the bot from Discord.
|
||||
* @param {Snowflake} [id='@me'] ID of application to fetch
|
||||
* @returns {Promise<OAuth2Application>}
|
||||
*/
|
||||
fetchApplication(id = '@me') {
|
||||
return this.rest.methods.getApplication(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a link that can be used to invite the bot to a guild.
|
||||
* <warn>This is only available when using a bot account.</warn>
|
||||
* @param {PermissionResolvable[]|number} [permissions] Permissions to request
|
||||
* @returns {Promise<string>}
|
||||
* @example
|
||||
* client.generateInvite(['SEND_MESSAGES', 'MANAGE_GUILD', 'MENTION_EVERYONE'])
|
||||
* .then(link => {
|
||||
* console.log(`Generated bot invite link: ${link}`);
|
||||
* });
|
||||
*/
|
||||
generateInvite(permissions) {
|
||||
if (permissions) {
|
||||
if (permissions instanceof Array) permissions = Permissions.resolve(permissions);
|
||||
} else {
|
||||
permissions = 0;
|
||||
}
|
||||
return this.fetchApplication().then(application =>
|
||||
`https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setTimeout(fn, delay, ...args) {
|
||||
const timeout = setTimeout(() => {
|
||||
fn(...args);
|
||||
this._timeouts.delete(timeout);
|
||||
}, delay);
|
||||
this._timeouts.add(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* @param {Timeout} timeout Timeout to cancel
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
clearTimeout(timeout);
|
||||
this._timeouts.delete(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setInterval(fn, delay, ...args) {
|
||||
const interval = setInterval(fn, delay, ...args);
|
||||
this._intervals.add(interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* @param {Timeout} interval Interval to cancel
|
||||
*/
|
||||
clearInterval(interval) {
|
||||
clearInterval(interval);
|
||||
this._intervals.delete(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a ping to {@link Client#pings}.
|
||||
* @param {number} startTime Starting time of the ping
|
||||
* @private
|
||||
*/
|
||||
_pong(startTime) {
|
||||
this.pings.unshift(Date.now() - startTime);
|
||||
if (this.pings.length > 3) this.pings.length = 3;
|
||||
this.ws.lastHeartbeatAck = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds/updates a friend's presence in {@link Client#presences}.
|
||||
* @param {Snowflake} id ID of the user
|
||||
* @param {Object} presence Raw presence object from Discord
|
||||
* @private
|
||||
*/
|
||||
_setPresence(id, presence) {
|
||||
if (this.presences.has(id)) {
|
||||
this.presences.get(id).update(presence);
|
||||
return;
|
||||
}
|
||||
this.presences.set(id, new Presence(presence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
|
||||
* with the client as `this`.
|
||||
* @param {string} script Script to eval
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
_eval(script) {
|
||||
return eval(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the client options.
|
||||
* @param {ClientOptions} [options=this.options] Options to validate
|
||||
* @private
|
||||
*/
|
||||
_validateOptions(options = this.options) {
|
||||
if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) {
|
||||
throw new TypeError('The shardCount option must be a number.');
|
||||
}
|
||||
if (typeof options.shardId !== 'number' || isNaN(options.shardId)) {
|
||||
throw new TypeError('The shardId option must be a number.');
|
||||
}
|
||||
if (options.shardCount < 0) throw new RangeError('The shardCount option must be at least 0.');
|
||||
if (options.shardId < 0) throw new RangeError('The shardId option must be at least 0.');
|
||||
if (options.shardId !== 0 && options.shardId >= options.shardCount) {
|
||||
throw new RangeError('The shardId option must be less than shardCount.');
|
||||
}
|
||||
if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) {
|
||||
throw new TypeError('The messageCacheMaxSize option must be a number.');
|
||||
}
|
||||
if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
|
||||
throw new TypeError('The messageCacheLifetime option must be a number.');
|
||||
}
|
||||
if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
|
||||
throw new TypeError('The messageSweepInterval option must be a number.');
|
||||
}
|
||||
if (typeof options.fetchAllMembers !== 'boolean') {
|
||||
throw new TypeError('The fetchAllMembers option must be a boolean.');
|
||||
}
|
||||
if (typeof options.disableEveryone !== 'boolean') {
|
||||
throw new TypeError('The disableEveryone option must be a boolean.');
|
||||
}
|
||||
if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
|
||||
throw new TypeError('The restWsBridgeTimeout option must be a number.');
|
||||
}
|
||||
if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
|
||||
/**
|
||||
* Emitted for general warnings.
|
||||
* @event Client#warn
|
||||
* @param {string} info The warning
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted for general debugging information.
|
||||
* @event Client#debug
|
||||
* @param {string} info The debug information
|
||||
*/
|
||||
136
node_modules/discord.js/src/client/ClientDataManager.js
generated
vendored
Normal file
136
node_modules/discord.js/src/client/ClientDataManager.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
const Constants = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
const Guild = require('../structures/Guild');
|
||||
const User = require('../structures/User');
|
||||
const CategoryChannel = require('../structures/CategoryChannel');
|
||||
const DMChannel = require('../structures/DMChannel');
|
||||
const Emoji = require('../structures/Emoji');
|
||||
const TextChannel = require('../structures/TextChannel');
|
||||
const VoiceChannel = require('../structures/VoiceChannel');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const GroupDMChannel = require('../structures/GroupDMChannel');
|
||||
|
||||
class ClientDataManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
get pastReady() {
|
||||
return this.client.ws.connection.status === Constants.Status.READY;
|
||||
}
|
||||
|
||||
newGuild(data) {
|
||||
const already = this.client.guilds.has(data.id);
|
||||
const guild = new Guild(this.client, data);
|
||||
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
|
||||
*/
|
||||
if (this.client.options.fetchAllMembers) {
|
||||
guild.fetchMembers().then(() => { this.client.emit(Constants.Events.GUILD_CREATE, guild); });
|
||||
} else {
|
||||
this.client.emit(Constants.Events.GUILD_CREATE, guild);
|
||||
}
|
||||
}
|
||||
|
||||
return guild;
|
||||
}
|
||||
|
||||
newUser(data) {
|
||||
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) {
|
||||
const already = this.client.channels.has(data.id);
|
||||
let channel;
|
||||
if (data.type === Constants.ChannelTypes.DM) {
|
||||
channel = new DMChannel(this.client, data);
|
||||
} else if (data.type === Constants.ChannelTypes.GROUP_DM) {
|
||||
channel = new GroupDMChannel(this.client, data);
|
||||
} else {
|
||||
guild = guild || this.client.guilds.get(data.guild_id);
|
||||
if (guild) {
|
||||
if (data.type === Constants.ChannelTypes.TEXT) {
|
||||
channel = new TextChannel(guild, data);
|
||||
guild.channels.set(channel.id, channel);
|
||||
} else if (data.type === Constants.ChannelTypes.VOICE) {
|
||||
channel = new VoiceChannel(guild, data);
|
||||
guild.channels.set(channel.id, channel);
|
||||
} else if (data.type === Constants.ChannelTypes.CATEGORY) {
|
||||
channel = new CategoryChannel(guild, data);
|
||||
guild.channels.set(channel.id, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel && !already) {
|
||||
if (this.pastReady) this.client.emit(Constants.Events.CHANNEL_CREATE, channel);
|
||||
this.client.channels.set(channel.id, channel);
|
||||
return channel;
|
||||
} else if (already) {
|
||||
return channel;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
newEmoji(data, guild) {
|
||||
const already = guild.emojis.has(data.id);
|
||||
if (data && !already) {
|
||||
let emoji = new Emoji(guild, data);
|
||||
this.client.emit(Constants.Events.GUILD_EMOJI_CREATE, emoji);
|
||||
guild.emojis.set(emoji.id, emoji);
|
||||
return emoji;
|
||||
} else if (already) {
|
||||
return guild.emojis.get(data.id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
killEmoji(emoji) {
|
||||
if (!(emoji instanceof Emoji && emoji.guild)) return;
|
||||
this.client.emit(Constants.Events.GUILD_EMOJI_DELETE, emoji);
|
||||
emoji.guild.emojis.delete(emoji.id);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
killUser(user) {
|
||||
this.client.users.delete(user.id);
|
||||
}
|
||||
|
||||
killChannel(channel) {
|
||||
this.client.channels.delete(channel.id);
|
||||
if (channel instanceof GuildChannel) channel.guild.channels.delete(channel.id);
|
||||
}
|
||||
|
||||
updateGuild(currentGuild, newData) {
|
||||
const oldGuild = Util.cloneObject(currentGuild);
|
||||
currentGuild.setup(newData);
|
||||
if (this.pastReady) this.client.emit(Constants.Events.GUILD_UPDATE, oldGuild, currentGuild);
|
||||
}
|
||||
|
||||
updateChannel(currentChannel, newData) {
|
||||
currentChannel.setup(newData);
|
||||
}
|
||||
|
||||
updateEmoji(currentEmoji, newData) {
|
||||
const oldEmoji = Util.cloneObject(currentEmoji);
|
||||
currentEmoji.setup(newData);
|
||||
this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
|
||||
return currentEmoji;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientDataManager;
|
||||
378
node_modules/discord.js/src/client/ClientDataResolver.js
generated
vendored
Normal file
378
node_modules/discord.js/src/client/ClientDataResolver.js
generated
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const snekfetch = require('snekfetch');
|
||||
|
||||
const Constants = require('../util/Constants');
|
||||
const convertToBuffer = require('../util/Util').convertToBuffer;
|
||||
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');
|
||||
const Emoji = require('../structures/Emoji');
|
||||
const ReactionEmoji = require('../structures/ReactionEmoji');
|
||||
const Role = require('../structures/Role');
|
||||
|
||||
/**
|
||||
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
|
||||
* extracting a User from a Message object.
|
||||
* @private
|
||||
*/
|
||||
class ClientDataResolver {
|
||||
/**
|
||||
* @param {Client} client The client the resolver is for
|
||||
*/
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that resolves to give a User object. This can be:
|
||||
* * A User object
|
||||
* * A Snowflake
|
||||
* * A Message object (resolves to the message author)
|
||||
* * A Guild object (owner of the guild)
|
||||
* * A GuildMember object
|
||||
* @typedef {User|Snowflake|Message|Guild|GuildMember} UserResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a UserResolvable to a User object.
|
||||
* @param {UserResolvable} user The UserResolvable to identify
|
||||
* @returns {?User}
|
||||
*/
|
||||
resolveUser(user) {
|
||||
if (user instanceof User) return user;
|
||||
if (typeof user === 'string') return this.client.users.get(user) || null;
|
||||
if (user instanceof GuildMember) return user.user;
|
||||
if (user instanceof Message) return user.author;
|
||||
if (user instanceof Guild) return user.owner;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a UserResolvable to a user ID string.
|
||||
* @param {UserResolvable} user The UserResolvable to identify
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveUserID(user) {
|
||||
if (user instanceof User || user instanceof GuildMember) return user.id;
|
||||
if (typeof user === 'string') return user || null;
|
||||
if (user instanceof Message) return user.author.id;
|
||||
if (user instanceof Guild) return user.ownerID;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that resolves to give a Guild object. This can be:
|
||||
* * A Guild object
|
||||
* * A Snowflake
|
||||
* @typedef {Guild|Snowflake} GuildResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a GuildResolvable to a Guild object.
|
||||
* @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) || null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that resolves to give a GuildMember object. This can be:
|
||||
* * A GuildMember object
|
||||
* * A User object
|
||||
* @typedef {GuildMember|User} GuildMemberResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @returns {?GuildMember}
|
||||
*/
|
||||
resolveGuildMember(guild, user) {
|
||||
if (user instanceof GuildMember) return user;
|
||||
guild = this.resolveGuild(guild);
|
||||
user = this.resolveUser(user);
|
||||
if (!guild || !user) return null;
|
||||
return guild.members.get(user.id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to a Role object. This can be:
|
||||
* * A Role
|
||||
* * A Snowflake
|
||||
* @typedef {Role|Snowflake} RoleResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a RoleResolvable to a Role object.
|
||||
* @param {GuildResolvable} guild The guild that this role is part of
|
||||
* @param {RoleResolvable} role The role resolvable to resolve
|
||||
* @returns {?Role}
|
||||
*/
|
||||
resolveRole(guild, role) {
|
||||
if (role instanceof Role) return role;
|
||||
guild = this.resolveGuild(guild);
|
||||
if (!guild) return null;
|
||||
if (typeof role === 'string') return guild.roles.get(role);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a Channel object. This can be:
|
||||
* * A Channel object
|
||||
* * A Message object (the channel the message was sent in)
|
||||
* * A Guild object (the #general channel)
|
||||
* * A Snowflake
|
||||
* @typedef {Channel|Guild|Message|Snowflake} ChannelResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a ChannelResolvable to a Channel object.
|
||||
* @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) || null;
|
||||
if (channel instanceof Message) return channel.channel;
|
||||
if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a ChannelResolvable to a channel ID.
|
||||
* @param {ChannelResolvable} channel The channel resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
resolveChannelID(channel) {
|
||||
if (channel instanceof Channel) return channel.id;
|
||||
if (typeof channel === 'string') return channel;
|
||||
if (channel instanceof Message) return channel.channel.id;
|
||||
if (channel instanceof Guild) return channel.defaultChannel.id;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give an invite code. This can be:
|
||||
* * An invite code
|
||||
* * An invite URL
|
||||
* @typedef {string} InviteResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves InviteResolvable to an invite code.
|
||||
* @param {InviteResolvable} data The invite resolvable to resolve
|
||||
* @returns {string}
|
||||
*/
|
||||
resolveInviteCode(data) {
|
||||
const inviteRegex = /discord(?:app\.com\/invite|\.gg)\/([\w-]{2,255})/i;
|
||||
const match = inviteRegex.exec(data);
|
||||
if (match && match[1]) return match[1];
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 value
|
||||
* @typedef {string|Array|*} StringResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a StringResolvable to a string.
|
||||
* @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');
|
||||
return String(data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
|
||||
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
|
||||
* @returns {Promise<?string>}
|
||||
*/
|
||||
resolveImage(image) {
|
||||
if (!image) return Promise.resolve(null);
|
||||
if (typeof image === 'string' && image.startsWith('data:')) {
|
||||
return Promise.resolve(image);
|
||||
}
|
||||
return this.resolveFile(image).then(this.resolveBase64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
|
||||
* * A Buffer
|
||||
* * A base64 string
|
||||
* @typedef {Buffer|string} Base64Resolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a Base64Resolvable to a Base 64 image.
|
||||
* @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')}`;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a Buffer. This can be:
|
||||
* * A Buffer
|
||||
* * The path to a local file
|
||||
* * A URL
|
||||
* * A Stream
|
||||
* @typedef {string|Buffer} BufferResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external Stream
|
||||
* @see {@link https://nodejs.org/api/stream.html}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a BufferResolvable to a Buffer.
|
||||
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
resolveFile(resource) {
|
||||
if (resource instanceof Buffer) return Promise.resolve(resource);
|
||||
if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource));
|
||||
|
||||
if (typeof resource === 'string') {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (/^https?:\/\//.test(resource)) {
|
||||
snekfetch.get(resource)
|
||||
.end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
if (!(res.body instanceof Buffer)) return reject(new TypeError('The response body isn\'t a Buffer.'));
|
||||
return resolve(res.body);
|
||||
});
|
||||
} else {
|
||||
const file = path.resolve(resource);
|
||||
fs.stat(file, (err, stats) => {
|
||||
if (err) return reject(err);
|
||||
if (!stats || !stats.isFile()) return reject(new Error(`The file could not be found: ${file}`));
|
||||
fs.readFile(file, (err2, data) => {
|
||||
if (err2) reject(err2); else resolve(data);
|
||||
});
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (resource.pipe && typeof resource.pipe === 'function') {
|
||||
return new Promise((resolve, reject) => {
|
||||
const buffers = [];
|
||||
resource.once('error', reject);
|
||||
resource.on('data', data => buffers.push(data));
|
||||
resource.once('end', () => resolve(Buffer.concat(buffers)));
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give an emoji identifier. This can be:
|
||||
* * The unicode representation of an emoji
|
||||
* * A custom emoji ID
|
||||
* * An Emoji object
|
||||
* * A ReactionEmoji object
|
||||
* @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves an EmojiResolvable to an emoji identifier.
|
||||
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
|
||||
* @returns {?string}
|
||||
*/
|
||||
resolveEmojiIdentifier(emoji) {
|
||||
if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
|
||||
if (typeof emoji === 'string') {
|
||||
if (this.client.emojis.has(emoji)) return this.client.emojis.get(emoji).identifier;
|
||||
else if (!emoji.includes('%')) return encodeURIComponent(emoji);
|
||||
else return emoji;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be a Hex Literal, Hex String, Number, RGB Array, or one of the following
|
||||
* ```
|
||||
* [
|
||||
* 'DEFAULT',
|
||||
* 'AQUA',
|
||||
* 'GREEN',
|
||||
* 'BLUE',
|
||||
* 'PURPLE',
|
||||
* 'GOLD',
|
||||
* 'ORANGE',
|
||||
* 'RED',
|
||||
* 'GREY',
|
||||
* 'DARKER_GREY',
|
||||
* 'NAVY',
|
||||
* 'DARK_AQUA',
|
||||
* 'DARK_GREEN',
|
||||
* 'DARK_BLUE',
|
||||
* 'DARK_PURPLE',
|
||||
* 'DARK_GOLD',
|
||||
* 'DARK_ORANGE',
|
||||
* 'DARK_RED',
|
||||
* 'DARK_GREY',
|
||||
* 'LIGHT_GREY',
|
||||
* 'DARK_NAVY',
|
||||
* 'RANDOM',
|
||||
* ]
|
||||
* ```
|
||||
* or something like
|
||||
* ```
|
||||
* [255, 0, 255]
|
||||
* ```
|
||||
* for purple
|
||||
* @typedef {string|number|Array} ColorResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves a ColorResolvable into a color number.
|
||||
* @param {ColorResolvable} color Color to resolve
|
||||
* @returns {number} A color
|
||||
*/
|
||||
static resolveColor(color) {
|
||||
if (typeof color === 'string') {
|
||||
if (color === 'RANDOM') return Math.floor(Math.random() * (0xFFFFFF + 1));
|
||||
if (color === 'DEFAULT') return 0;
|
||||
color = Constants.Colors[color] || parseInt(color.replace('#', ''), 16);
|
||||
} else if (color instanceof Array) {
|
||||
color = (color[0] << 16) + (color[1] << 8) + color[2];
|
||||
}
|
||||
|
||||
if (color < 0 || color > 0xFFFFFF) {
|
||||
throw new RangeError('Color must be within the range 0 - 16777215 (0xFFFFFF).');
|
||||
} else if (color && isNaN(color)) {
|
||||
throw new TypeError('Unable to convert color to a number.');
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ColorResolvable} color Color to resolve
|
||||
* @returns {number} A color
|
||||
*/
|
||||
resolveColor(color) {
|
||||
return this.constructor.resolveColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientDataResolver;
|
||||
73
node_modules/discord.js/src/client/ClientManager.js
generated
vendored
Normal file
73
node_modules/discord.js/src/client/ClientManager.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
const Constants = require('../util/Constants');
|
||||
const WebSocketConnection = require('./websocket/WebSocketConnection');
|
||||
|
||||
/**
|
||||
* Manages the state and background tasks of the client.
|
||||
* @private
|
||||
*/
|
||||
class ClientManager {
|
||||
constructor(client) {
|
||||
/**
|
||||
* The client that instantiated this Manager
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = client;
|
||||
|
||||
/**
|
||||
* The heartbeat interval
|
||||
* @type {?number}
|
||||
*/
|
||||
this.heartbeatInterval = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of the client
|
||||
* @type {number}
|
||||
*/
|
||||
get status() {
|
||||
return this.connection ? this.connection.status : Constants.Status.IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
connectToWebSocket(token, resolve, reject) {
|
||||
this.client.emit(Constants.Events.DEBUG, `Authenticated using token ${token}`);
|
||||
this.client.token = token;
|
||||
const timeout = this.client.setTimeout(() => reject(new Error(Constants.Errors.TOOK_TOO_LONG)), 1000 * 300);
|
||||
this.client.rest.methods.getGateway().then(res => {
|
||||
const protocolVersion = Constants.DefaultOptions.ws.version;
|
||||
const gateway = `${res.url}/?v=${protocolVersion}&encoding=${WebSocketConnection.ENCODING}`;
|
||||
this.client.emit(Constants.Events.DEBUG, `Using gateway ${gateway}`);
|
||||
this.client.ws.connect(gateway);
|
||||
this.client.ws.connection.once('close', event => {
|
||||
if (event.code === 4004) reject(new Error(Constants.Errors.BAD_LOGIN));
|
||||
if (event.code === 4010) reject(new Error(Constants.Errors.INVALID_SHARD));
|
||||
if (event.code === 4011) reject(new Error(Constants.Errors.SHARDING_REQUIRED));
|
||||
});
|
||||
this.client.once(Constants.Events.READY, () => {
|
||||
resolve(token);
|
||||
this.client.clearTimeout(timeout);
|
||||
});
|
||||
}, reject);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.client.ws.destroy();
|
||||
this.client.rest.destroy();
|
||||
if (!this.client.user) return Promise.resolve();
|
||||
if (this.client.user.bot) {
|
||||
this.client.token = null;
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return this.client.rest.methods.logout().then(() => {
|
||||
this.client.token = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientManager;
|
||||
118
node_modules/discord.js/src/client/WebhookClient.js
generated
vendored
Normal file
118
node_modules/discord.js/src/client/WebhookClient.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
const Webhook = require('../structures/Webhook');
|
||||
const RESTManager = require('./rest/RESTManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const Constants = require('../util/Constants');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* The webhook client.
|
||||
* @extends {Webhook}
|
||||
*/
|
||||
class WebhookClient extends Webhook {
|
||||
/**
|
||||
* @param {Snowflake} id ID of the webhook
|
||||
* @param {string} token Token of the webhook
|
||||
* @param {ClientOptions} [options] Options for the client
|
||||
* @example
|
||||
* // Create a new webhook and send a message
|
||||
* const hook = new Discord.WebhookClient('1234', 'abcdef');
|
||||
* hook.sendMessage('This will send a message').catch(console.error);
|
||||
*/
|
||||
constructor(id, token, options) {
|
||||
super(null, id, token);
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = Util.mergeDefault(Constants.DefaultOptions, options);
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
* @type {RESTManager}
|
||||
* @private
|
||||
*/
|
||||
this.rest = new RESTManager(this);
|
||||
|
||||
/**
|
||||
* The data resolver of the client
|
||||
* @type {ClientDataResolver}
|
||||
* @private
|
||||
*/
|
||||
this.resolver = new ClientDataResolver(this);
|
||||
|
||||
/**
|
||||
* Timeouts set by {@link WebhookClient#setTimeout} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._timeouts = new Set();
|
||||
|
||||
/**
|
||||
* Intervals set by {@link WebhookClient#setInterval} that are still active
|
||||
* @type {Set<Timeout>}
|
||||
* @private
|
||||
*/
|
||||
this._intervals = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setTimeout(fn, delay, ...args) {
|
||||
const timeout = setTimeout(() => {
|
||||
fn(...args);
|
||||
this._timeouts.delete(timeout);
|
||||
}, delay);
|
||||
this._timeouts.add(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* @param {Timeout} timeout Timeout to cancel
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
clearTimeout(timeout);
|
||||
this._timeouts.delete(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval that will be automatically cancelled if the client is destroyed.
|
||||
* @param {Function} fn Function to execute
|
||||
* @param {number} delay Time to wait before executing (in milliseconds)
|
||||
* @param {...*} args Arguments for the function
|
||||
* @returns {Timeout}
|
||||
*/
|
||||
setInterval(fn, delay, ...args) {
|
||||
const interval = setInterval(fn, delay, ...args);
|
||||
this._intervals.add(interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* @param {Timeout} interval Interval to cancel
|
||||
*/
|
||||
clearInterval(interval) {
|
||||
clearInterval(interval);
|
||||
this._intervals.delete(interval);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroys the client.
|
||||
*/
|
||||
destroy() {
|
||||
for (const t of this._timeouts) clearTimeout(t);
|
||||
for (const i of this._intervals) clearInterval(i);
|
||||
this._timeouts.clear();
|
||||
this._intervals.clear();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebhookClient;
|
||||
23
node_modules/discord.js/src/client/actions/Action.js
generated
vendored
Normal file
23
node_modules/discord.js/src/client/actions/Action.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
|
||||
ABOUT ACTIONS
|
||||
|
||||
Actions are similar to WebSocket Packet Handlers, but since introducing
|
||||
the REST API methods, in order to prevent rewriting code to handle data,
|
||||
"actions" have been introduced. They're basically what Packet Handlers
|
||||
used to be but they're strictly for manipulating data and making sure
|
||||
that WebSocket events don't clash with REST methods.
|
||||
|
||||
*/
|
||||
|
||||
class GenericAction {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GenericAction;
|
||||
40
node_modules/discord.js/src/client/actions/ActionsManager.js
generated
vendored
Normal file
40
node_modules/discord.js/src/client/actions/ActionsManager.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
class ActionsManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
|
||||
this.register(require('./MessageCreate'));
|
||||
this.register(require('./MessageDelete'));
|
||||
this.register(require('./MessageDeleteBulk'));
|
||||
this.register(require('./MessageUpdate'));
|
||||
this.register(require('./MessageReactionAdd'));
|
||||
this.register(require('./MessageReactionRemove'));
|
||||
this.register(require('./MessageReactionRemoveAll'));
|
||||
this.register(require('./ChannelCreate'));
|
||||
this.register(require('./ChannelDelete'));
|
||||
this.register(require('./ChannelUpdate'));
|
||||
this.register(require('./GuildDelete'));
|
||||
this.register(require('./GuildUpdate'));
|
||||
this.register(require('./GuildMemberGet'));
|
||||
this.register(require('./GuildMemberRemove'));
|
||||
this.register(require('./GuildBanRemove'));
|
||||
this.register(require('./GuildRoleCreate'));
|
||||
this.register(require('./GuildRoleDelete'));
|
||||
this.register(require('./GuildRoleUpdate'));
|
||||
this.register(require('./UserGet'));
|
||||
this.register(require('./UserUpdate'));
|
||||
this.register(require('./UserNoteUpdate'));
|
||||
this.register(require('./GuildSync'));
|
||||
this.register(require('./GuildEmojiCreate'));
|
||||
this.register(require('./GuildEmojiDelete'));
|
||||
this.register(require('./GuildEmojiUpdate'));
|
||||
this.register(require('./GuildEmojisUpdate'));
|
||||
this.register(require('./GuildRolesPositionUpdate'));
|
||||
this.register(require('./GuildChannelsPositionUpdate'));
|
||||
}
|
||||
|
||||
register(Action) {
|
||||
this[Action.name.replace(/Action$/, '')] = new Action(this.client);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ActionsManager;
|
||||
11
node_modules/discord.js/src/client/actions/ChannelCreate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/actions/ChannelCreate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class ChannelCreateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const channel = client.dataManager.newChannel(data);
|
||||
return { channel };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ChannelCreateAction;
|
||||
29
node_modules/discord.js/src/client/actions/ChannelDelete.js
generated
vendored
Normal file
29
node_modules/discord.js/src/client/actions/ChannelDelete.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class ChannelDeleteAction extends Action {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
let channel = client.channels.get(data.id);
|
||||
if (channel) {
|
||||
client.dataManager.killChannel(channel);
|
||||
this.deleted.set(channel.id, channel);
|
||||
this.scheduleForDeletion(channel.id);
|
||||
} else {
|
||||
channel = this.deleted.get(data.id) || null;
|
||||
}
|
||||
|
||||
return { channel };
|
||||
}
|
||||
|
||||
scheduleForDeletion(id) {
|
||||
this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ChannelDeleteAction;
|
||||
34
node_modules/discord.js/src/client/actions/ChannelUpdate.js
generated
vendored
Normal file
34
node_modules/discord.js/src/client/actions/ChannelUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
class ChannelUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const channel = client.channels.get(data.id);
|
||||
if (channel) {
|
||||
const oldChannel = Util.cloneObject(channel);
|
||||
channel.setup(data);
|
||||
client.emit(Constants.Events.CHANNEL_UPDATE, oldChannel, channel);
|
||||
return {
|
||||
old: oldChannel,
|
||||
updated: channel,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: null,
|
||||
updated: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
13
node_modules/discord.js/src/client/actions/GuildBanRemove.js
generated
vendored
Normal file
13
node_modules/discord.js/src/client/actions/GuildBanRemove.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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);
|
||||
const user = client.dataManager.newUser(data.user);
|
||||
if (guild && user) client.emit(Constants.Events.GUILD_BAN_REMOVE, guild, user);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildBanRemove;
|
||||
19
node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/actions/GuildChannelsPositionUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildChannelsPositionUpdate extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
if (guild) {
|
||||
for (const partialChannel of data.channels) {
|
||||
const channel = guild.channels.get(partialChannel.id);
|
||||
if (channel) channel.position = partialChannel.position;
|
||||
}
|
||||
}
|
||||
|
||||
return { guild };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildChannelsPositionUpdate;
|
||||
56
node_modules/discord.js/src/client/actions/GuildDelete.js
generated
vendored
Normal file
56
node_modules/discord.js/src/client/actions/GuildDelete.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class GuildDeleteAction extends Action {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
let guild = client.guilds.get(data.id);
|
||||
if (guild) {
|
||||
for (const channel of guild.channels.values()) {
|
||||
if (channel.type === 'text') channel.stopTyping(true);
|
||||
}
|
||||
|
||||
if (guild.available && data.unavailable) {
|
||||
// Guild is unavailable
|
||||
guild.available = false;
|
||||
client.emit(Constants.Events.GUILD_UNAVAILABLE, guild);
|
||||
|
||||
// Stops the GuildDelete packet thinking a guild was actually deleted,
|
||||
// handles emitting of event itself
|
||||
return {
|
||||
guild: null,
|
||||
};
|
||||
}
|
||||
|
||||
for (const channel of guild.channels.values()) this.client.channels.delete(channel.id);
|
||||
if (guild.voiceConnection) guild.voiceConnection.disconnect();
|
||||
|
||||
// Delete guild
|
||||
client.guilds.delete(guild.id);
|
||||
this.deleted.set(guild.id, guild);
|
||||
this.scheduleForDeletion(guild.id);
|
||||
} else {
|
||||
guild = this.deleted.get(data.id) || null;
|
||||
}
|
||||
|
||||
return { guild };
|
||||
}
|
||||
|
||||
scheduleForDeletion(id) {
|
||||
this.client.setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild becomes unavailable, likely due to a server outage.
|
||||
* @event Client#guildUnavailable
|
||||
* @param {Guild} guild The guild that has become unavailable
|
||||
*/
|
||||
|
||||
module.exports = GuildDeleteAction;
|
||||
17
node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/actions/GuildEmojiCreate.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildEmojiCreateAction extends Action {
|
||||
handle(guild, createdEmoji) {
|
||||
const client = this.client;
|
||||
const emoji = client.dataManager.newEmoji(createdEmoji, guild);
|
||||
return { emoji };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a custom emoji is created in a guild.
|
||||
* @event Client#emojiCreate
|
||||
* @param {Emoji} emoji The emoji that was created
|
||||
*/
|
||||
|
||||
module.exports = GuildEmojiCreateAction;
|
||||
17
node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/actions/GuildEmojiDelete.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildEmojiDeleteAction extends Action {
|
||||
handle(emoji) {
|
||||
const client = this.client;
|
||||
client.dataManager.killEmoji(emoji);
|
||||
return { emoji };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a custom guild emoji is deleted.
|
||||
* @event Client#emojiDelete
|
||||
* @param {Emoji} emoji The emoji that was deleted
|
||||
*/
|
||||
|
||||
module.exports = GuildEmojiDeleteAction;
|
||||
17
node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/actions/GuildEmojiUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildEmojiUpdateAction extends Action {
|
||||
handle(oldEmoji, newEmoji) {
|
||||
const emoji = this.client.dataManager.updateEmoji(oldEmoji, newEmoji);
|
||||
return { emoji };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a custom guild emoji is updated.
|
||||
* @event Client#emojiUpdate
|
||||
* @param {Emoji} oldEmoji The old emoji
|
||||
* @param {Emoji} newEmoji The new emoji
|
||||
*/
|
||||
|
||||
module.exports = GuildEmojiUpdateAction;
|
||||
38
node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js
generated
vendored
Normal file
38
node_modules/discord.js/src/client/actions/GuildEmojisUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
function mappify(iterable) {
|
||||
const map = new Map();
|
||||
for (const x of iterable) map.set(...x);
|
||||
return map;
|
||||
}
|
||||
|
||||
class GuildEmojisUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const guild = this.client.guilds.get(data.guild_id);
|
||||
if (!guild || !guild.emojis) return;
|
||||
|
||||
const deletions = mappify(guild.emojis.entries());
|
||||
|
||||
for (const emoji of data.emojis) {
|
||||
// Determine type of emoji event
|
||||
const cachedEmoji = guild.emojis.get(emoji.id);
|
||||
if (cachedEmoji) {
|
||||
deletions.delete(emoji.id);
|
||||
if (!cachedEmoji.equals(emoji, true)) {
|
||||
// Emoji updated
|
||||
this.client.actions.GuildEmojiUpdate.handle(cachedEmoji, emoji);
|
||||
}
|
||||
} else {
|
||||
// Emoji added
|
||||
this.client.actions.GuildEmojiCreate.handle(guild, emoji);
|
||||
}
|
||||
}
|
||||
|
||||
for (const emoji of deletions.values()) {
|
||||
// Emoji deleted
|
||||
this.client.actions.GuildEmojiDelete.handle(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildEmojisUpdateAction;
|
||||
10
node_modules/discord.js/src/client/actions/GuildMemberGet.js
generated
vendored
Normal file
10
node_modules/discord.js/src/client/actions/GuildMemberGet.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildMemberGetAction extends Action {
|
||||
handle(guild, data) {
|
||||
const member = guild._addMember(data, false);
|
||||
return { member };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildMemberGetAction;
|
||||
40
node_modules/discord.js/src/client/actions/GuildMemberRemove.js
generated
vendored
Normal file
40
node_modules/discord.js/src/client/actions/GuildMemberRemove.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class GuildMemberRemoveAction extends Action {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
let member = null;
|
||||
if (guild) {
|
||||
member = guild.members.get(data.user.id);
|
||||
if (member) {
|
||||
guild.memberCount--;
|
||||
guild._removeMember(member);
|
||||
this.deleted.set(guild.id + data.user.id, member);
|
||||
if (client.status === Constants.Status.READY) client.emit(Constants.Events.GUILD_MEMBER_REMOVE, member);
|
||||
this.scheduleForDeletion(guild.id, data.user.id);
|
||||
} else {
|
||||
member = this.deleted.get(guild.id + data.user.id) || null;
|
||||
}
|
||||
}
|
||||
return { guild, member };
|
||||
}
|
||||
|
||||
scheduleForDeletion(guildID, userID) {
|
||||
this.client.setTimeout(() => this.deleted.delete(guildID + userID), this.client.options.restWsBridgeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a member leaves a guild, or is kicked.
|
||||
* @event Client#guildMemberRemove
|
||||
* @param {GuildMember} member The member that has left/been kicked from the guild
|
||||
*/
|
||||
|
||||
module.exports = GuildMemberRemoveAction;
|
||||
26
node_modules/discord.js/src/client/actions/GuildRoleCreate.js
generated
vendored
Normal file
26
node_modules/discord.js/src/client/actions/GuildRoleCreate.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Role = require('../../structures/Role');
|
||||
|
||||
class GuildRoleCreate extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
let role;
|
||||
if (guild) {
|
||||
const already = guild.roles.has(data.role.id);
|
||||
role = new Role(guild, data.role);
|
||||
guild.roles.set(role.id, role);
|
||||
if (!already) client.emit(Constants.Events.GUILD_ROLE_CREATE, role);
|
||||
}
|
||||
return { role };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a role is created.
|
||||
* @event Client#roleCreate
|
||||
* @param {Role} role The role that was created
|
||||
*/
|
||||
|
||||
module.exports = GuildRoleCreate;
|
||||
41
node_modules/discord.js/src/client/actions/GuildRoleDelete.js
generated
vendored
Normal file
41
node_modules/discord.js/src/client/actions/GuildRoleDelete.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class GuildRoleDeleteAction extends Action {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
let role;
|
||||
|
||||
if (guild) {
|
||||
role = guild.roles.get(data.role_id);
|
||||
if (role) {
|
||||
guild.roles.delete(data.role_id);
|
||||
this.deleted.set(guild.id + data.role_id, role);
|
||||
this.scheduleForDeletion(guild.id, data.role_id);
|
||||
client.emit(Constants.Events.GUILD_ROLE_DELETE, role);
|
||||
} else {
|
||||
role = this.deleted.get(guild.id + data.role_id) || null;
|
||||
}
|
||||
}
|
||||
|
||||
return { role };
|
||||
}
|
||||
|
||||
scheduleForDeletion(guildID, roleID) {
|
||||
this.client.setTimeout(() => this.deleted.delete(guildID + roleID), this.client.options.restWsBridgeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild role is deleted.
|
||||
* @event Client#roleDelete
|
||||
* @param {Role} role The role that was deleted
|
||||
*/
|
||||
|
||||
module.exports = GuildRoleDeleteAction;
|
||||
41
node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
generated
vendored
Normal file
41
node_modules/discord.js/src/client/actions/GuildRoleUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
class GuildRoleUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
|
||||
if (guild) {
|
||||
const roleData = data.role;
|
||||
let oldRole = null;
|
||||
|
||||
const role = guild.roles.get(roleData.id);
|
||||
if (role) {
|
||||
oldRole = Util.cloneObject(role);
|
||||
role.setup(data.role);
|
||||
client.emit(Constants.Events.GUILD_ROLE_UPDATE, oldRole, role);
|
||||
}
|
||||
|
||||
return {
|
||||
old: oldRole,
|
||||
updated: role,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: null,
|
||||
updated: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild role is updated.
|
||||
* @event Client#roleUpdate
|
||||
* @param {Role} oldRole The role before the update
|
||||
* @param {Role} newRole The role after the update
|
||||
*/
|
||||
|
||||
module.exports = GuildRoleUpdateAction;
|
||||
19
node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/actions/GuildRolesPositionUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildRolesPositionUpdate extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
if (guild) {
|
||||
for (const partialRole of data.roles) {
|
||||
const role = guild.roles.get(partialRole.id);
|
||||
if (role) role.position = partialRole.position;
|
||||
}
|
||||
}
|
||||
|
||||
return { guild };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildRolesPositionUpdate;
|
||||
29
node_modules/discord.js/src/client/actions/GuildSync.js
generated
vendored
Normal file
29
node_modules/discord.js/src/client/actions/GuildSync.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class GuildSync extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const guild = client.guilds.get(data.id);
|
||||
if (guild) {
|
||||
if (data.presences) {
|
||||
for (const presence of data.presences) guild._setPresence(presence.user.id, presence);
|
||||
}
|
||||
|
||||
if (data.members) {
|
||||
for (const syncMember of data.members) {
|
||||
const member = guild.members.get(syncMember.user.id);
|
||||
if (member) {
|
||||
guild._updateMember(member, syncMember);
|
||||
} else {
|
||||
guild._addMember(syncMember, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('large' in data) guild.large = data.large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildSync;
|
||||
34
node_modules/discord.js/src/client/actions/GuildUpdate.js
generated
vendored
Normal file
34
node_modules/discord.js/src/client/actions/GuildUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
class GuildUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const guild = client.guilds.get(data.id);
|
||||
if (guild) {
|
||||
const oldGuild = Util.cloneObject(guild);
|
||||
guild.setup(data);
|
||||
client.emit(Constants.Events.GUILD_UPDATE, oldGuild, guild);
|
||||
return {
|
||||
old: oldGuild,
|
||||
updated: guild,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: null,
|
||||
updated: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
55
node_modules/discord.js/src/client/actions/MessageCreate.js
generated
vendored
Normal file
55
node_modules/discord.js/src/client/actions/MessageCreate.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
const Action = require('./Action');
|
||||
const Message = require('../../structures/Message');
|
||||
|
||||
class MessageCreateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const channel = client.channels.get((data instanceof Array ? data[0] : data).channel_id);
|
||||
const user = client.users.get((data instanceof Array ? data[0] : data).author.id);
|
||||
if (channel) {
|
||||
const member = channel.guild ? channel.guild.member(user) : null;
|
||||
if (data instanceof Array) {
|
||||
const messages = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
messages[i] = channel._cacheMessage(new Message(channel, data[i], client));
|
||||
}
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
channel.lastMessageID = lastMessage.id;
|
||||
channel.lastMessage = lastMessage;
|
||||
if (user) {
|
||||
user.lastMessageID = lastMessage.id;
|
||||
user.lastMessage = lastMessage;
|
||||
}
|
||||
if (member) {
|
||||
member.lastMessageID = lastMessage.id;
|
||||
member.lastMessage = lastMessage;
|
||||
}
|
||||
return {
|
||||
messages,
|
||||
};
|
||||
} else {
|
||||
const message = channel._cacheMessage(new Message(channel, data, client));
|
||||
channel.lastMessageID = data.id;
|
||||
channel.lastMessage = message;
|
||||
if (user) {
|
||||
user.lastMessageID = data.id;
|
||||
user.lastMessage = message;
|
||||
}
|
||||
if (member) {
|
||||
member.lastMessageID = data.id;
|
||||
member.lastMessage = message;
|
||||
}
|
||||
return {
|
||||
message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageCreateAction;
|
||||
34
node_modules/discord.js/src/client/actions/MessageDelete.js
generated
vendored
Normal file
34
node_modules/discord.js/src/client/actions/MessageDelete.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class MessageDeleteAction extends Action {
|
||||
constructor(client) {
|
||||
super(client);
|
||||
this.deleted = new Map();
|
||||
}
|
||||
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const channel = client.channels.get(data.channel_id);
|
||||
let message;
|
||||
|
||||
if (channel) {
|
||||
message = channel.messages.get(data.id);
|
||||
if (message) {
|
||||
channel.messages.delete(message.id);
|
||||
this.deleted.set(channel.id + message.id, message);
|
||||
this.scheduleForDeletion(channel.id, message.id);
|
||||
} else {
|
||||
message = this.deleted.get(channel.id + data.id) || null;
|
||||
}
|
||||
}
|
||||
|
||||
return { message };
|
||||
}
|
||||
|
||||
scheduleForDeletion(channelID, messageID) {
|
||||
this.client.setTimeout(() => this.deleted.delete(channelID + messageID),
|
||||
this.client.options.restWsBridgeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageDeleteAction;
|
||||
26
node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
generated
vendored
Normal file
26
node_modules/discord.js/src/client/actions/MessageDeleteBulk.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
const Action = require('./Action');
|
||||
const Collection = require('../../util/Collection');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class MessageDeleteBulkAction extends Action {
|
||||
handle(data) {
|
||||
const messages = new Collection();
|
||||
|
||||
if (!data.messages) {
|
||||
const channel = this.client.channels.get(data.channel_id);
|
||||
for (const id of data.ids) {
|
||||
const message = channel.messages.get(id);
|
||||
if (message) messages.set(message.id, message);
|
||||
}
|
||||
} else {
|
||||
for (const msg of data.messages) {
|
||||
messages.set(msg.id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.size > 0) this.client.emit(Constants.Events.MESSAGE_BULK_DELETE, messages);
|
||||
return { messages };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageDeleteBulkAction;
|
||||
37
node_modules/discord.js/src/client/actions/MessageReactionAdd.js
generated
vendored
Normal file
37
node_modules/discord.js/src/client/actions/MessageReactionAdd.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
/*
|
||||
{ user_id: 'id',
|
||||
message_id: 'id',
|
||||
emoji: { name: '<27>', id: null },
|
||||
channel_id: 'id' } }
|
||||
*/
|
||||
|
||||
class MessageReactionAdd extends Action {
|
||||
handle(data) {
|
||||
const user = this.client.users.get(data.user_id);
|
||||
if (!user) return false;
|
||||
// Verify channel
|
||||
const channel = this.client.channels.get(data.channel_id);
|
||||
if (!channel || channel.type === 'voice') return false;
|
||||
// Verify message
|
||||
const message = channel.messages.get(data.message_id);
|
||||
if (!message) return false;
|
||||
if (!data.emoji) return false;
|
||||
// Verify reaction
|
||||
const reaction = message._addReaction(data.emoji, user);
|
||||
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_ADD, reaction, user);
|
||||
|
||||
return { message, reaction, user };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a reaction is added to a message.
|
||||
* @event Client#messageReactionAdd
|
||||
* @param {MessageReaction} messageReaction The reaction object
|
||||
* @param {User} user The user that applied the emoji or reaction emoji
|
||||
*/
|
||||
|
||||
module.exports = MessageReactionAdd;
|
||||
37
node_modules/discord.js/src/client/actions/MessageReactionRemove.js
generated
vendored
Normal file
37
node_modules/discord.js/src/client/actions/MessageReactionRemove.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
/*
|
||||
{ user_id: 'id',
|
||||
message_id: 'id',
|
||||
emoji: { name: '<27>', id: null },
|
||||
channel_id: 'id' } }
|
||||
*/
|
||||
|
||||
class MessageReactionRemove extends Action {
|
||||
handle(data) {
|
||||
const user = this.client.users.get(data.user_id);
|
||||
if (!user) return false;
|
||||
// Verify channel
|
||||
const channel = this.client.channels.get(data.channel_id);
|
||||
if (!channel || channel.type === 'voice') return false;
|
||||
// Verify message
|
||||
const message = channel.messages.get(data.message_id);
|
||||
if (!message) return false;
|
||||
if (!data.emoji) return false;
|
||||
// Verify reaction
|
||||
const reaction = message._removeReaction(data.emoji, user);
|
||||
if (reaction) this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE, reaction, user);
|
||||
|
||||
return { message, reaction, user };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a reaction is removed from a message.
|
||||
* @event Client#messageReactionRemove
|
||||
* @param {MessageReaction} messageReaction The reaction object
|
||||
* @param {User} user The user that removed the emoji or reaction emoji
|
||||
*/
|
||||
|
||||
module.exports = MessageReactionRemove;
|
||||
25
node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
generated
vendored
Normal file
25
node_modules/discord.js/src/client/actions/MessageReactionRemoveAll.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class MessageReactionRemoveAll extends Action {
|
||||
handle(data) {
|
||||
const channel = this.client.channels.get(data.channel_id);
|
||||
if (!channel || channel.type === 'voice') return false;
|
||||
|
||||
const message = channel.messages.get(data.message_id);
|
||||
if (!message) return false;
|
||||
|
||||
message._clearReactions();
|
||||
this.client.emit(Constants.Events.MESSAGE_REACTION_REMOVE_ALL, message);
|
||||
|
||||
return { message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever all reactions are removed from a message.
|
||||
* @event Client#messageReactionRemoveAll
|
||||
* @param {Message} message The message the reactions were removed from
|
||||
*/
|
||||
|
||||
module.exports = MessageReactionRemoveAll;
|
||||
40
node_modules/discord.js/src/client/actions/MessageUpdate.js
generated
vendored
Normal file
40
node_modules/discord.js/src/client/actions/MessageUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class MessageUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const channel = client.channels.get(data.channel_id);
|
||||
if (channel) {
|
||||
const message = channel.messages.get(data.id);
|
||||
if (message) {
|
||||
message.patch(data);
|
||||
client.emit(Constants.Events.MESSAGE_UPDATE, message._edits[0], message);
|
||||
return {
|
||||
old: message._edits[0],
|
||||
updated: message,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: message,
|
||||
updated: message,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: null,
|
||||
updated: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
11
node_modules/discord.js/src/client/actions/UserGet.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/actions/UserGet.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const Action = require('./Action');
|
||||
|
||||
class UserGetAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const user = client.dataManager.newUser(data);
|
||||
return { user };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserGetAction;
|
||||
30
node_modules/discord.js/src/client/actions/UserNoteUpdate.js
generated
vendored
Normal file
30
node_modules/discord.js/src/client/actions/UserNoteUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class UserNoteUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const oldNote = client.user.notes.get(data.id);
|
||||
const note = data.note.length ? data.note : null;
|
||||
|
||||
client.user.notes.set(data.id, note);
|
||||
|
||||
client.emit(Constants.Events.USER_NOTE_UPDATE, data.id, oldNote, note);
|
||||
|
||||
return {
|
||||
old: oldNote,
|
||||
updated: note,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a note is updated.
|
||||
* @event Client#userNoteUpdate
|
||||
* @param {User} user The user the note belongs to
|
||||
* @param {string} oldNote The note content before the update
|
||||
* @param {string} newNote The note content after the update
|
||||
*/
|
||||
|
||||
module.exports = UserNoteUpdateAction;
|
||||
33
node_modules/discord.js/src/client/actions/UserUpdate.js
generated
vendored
Normal file
33
node_modules/discord.js/src/client/actions/UserUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
class UserUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
if (client.user) {
|
||||
if (client.user.equals(data)) {
|
||||
return {
|
||||
old: client.user,
|
||||
updated: client.user,
|
||||
};
|
||||
}
|
||||
|
||||
const oldUser = Util.cloneObject(client.user);
|
||||
client.user.patch(data);
|
||||
client.emit(Constants.Events.USER_UPDATE, oldUser, client.user);
|
||||
return {
|
||||
old: oldUser,
|
||||
updated: client.user,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
old: null,
|
||||
updated: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserUpdateAction;
|
||||
52
node_modules/discord.js/src/client/rest/APIRequest.js
generated
vendored
Normal file
52
node_modules/discord.js/src/client/rest/APIRequest.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
const snekfetch = require('snekfetch');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class APIRequest {
|
||||
constructor(rest, method, path, auth, data, files, reason) {
|
||||
this.rest = rest;
|
||||
this.client = rest.client;
|
||||
this.method = method;
|
||||
this.path = path.toString();
|
||||
this.auth = auth;
|
||||
this.data = data;
|
||||
this.files = files;
|
||||
this.route = this.getRoute(this.path);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
getRoute(url) {
|
||||
let route = url.split('?')[0];
|
||||
if (route.includes('/channels/') || route.includes('/guilds/')) {
|
||||
const startInd = route.includes('/channels/') ? route.indexOf('/channels/') : route.indexOf('/guilds/');
|
||||
const majorID = route.substring(startInd).split('/')[2];
|
||||
route = route.replace(/(\d{8,})/g, ':id').replace(':id', majorID);
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
getAuth() {
|
||||
if (this.client.token && this.client.user && this.client.user.bot) {
|
||||
return `Bot ${this.client.token}`;
|
||||
} else if (this.client.token) {
|
||||
return this.client.token;
|
||||
}
|
||||
throw new Error(Constants.Errors.NO_TOKEN);
|
||||
}
|
||||
|
||||
gen() {
|
||||
const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`;
|
||||
const request = snekfetch[this.method](`${API}${this.path}`);
|
||||
if (this.auth) request.set('Authorization', this.getAuth());
|
||||
if (this.reason) request.set('X-Audit-Log-Reason', encodeURIComponent(this.reason));
|
||||
if (!this.rest.client.browser) request.set('User-Agent', this.rest.userAgentManager.userAgent);
|
||||
if (this.files) {
|
||||
for (const file of this.files) if (file && file.file) request.attach(file.name, file.file, file.name);
|
||||
if (typeof this.data !== 'undefined') request.attach('payload_json', JSON.stringify(this.data));
|
||||
} else if (this.data) {
|
||||
request.send(this.data);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIRequest;
|
||||
54
node_modules/discord.js/src/client/rest/DiscordAPIError.js
generated
vendored
Normal file
54
node_modules/discord.js/src/client/rest/DiscordAPIError.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Represents an error from the Discord API.
|
||||
* @extends Error
|
||||
*/
|
||||
class DiscordAPIError extends Error {
|
||||
constructor(path, error) {
|
||||
super();
|
||||
const flattened = this.constructor.flattenErrors(error.errors || error).join('\n');
|
||||
this.name = 'DiscordAPIError';
|
||||
this.message = error.message && flattened ? `${error.message}\n${flattened}` : error.message || flattened;
|
||||
|
||||
/**
|
||||
* The path of the request relative to the HTTP endpoint
|
||||
* @type {string}
|
||||
*/
|
||||
this.path = path;
|
||||
|
||||
/**
|
||||
* HTTP error code returned by Discord
|
||||
* @type {number}
|
||||
*/
|
||||
this.code = error.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an errors object returned from the API into an array.
|
||||
* @param {Object} obj Discord errors object
|
||||
* @param {string} [key] Used internally to determine key names of nested fields
|
||||
* @returns {string[]}
|
||||
* @private
|
||||
*/
|
||||
static flattenErrors(obj, key = '') {
|
||||
let messages = [];
|
||||
|
||||
for (const k of Object.keys(obj)) {
|
||||
if (k === 'message') continue;
|
||||
const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;
|
||||
|
||||
if (obj[k]._errors) {
|
||||
messages.push(`${newKey}: ${obj[k]._errors.map(e => e.message).join(' ')}`);
|
||||
} else if (obj[k].code || obj[k].message) {
|
||||
messages.push(`${obj[k].code ? `${obj[k].code}: ` : ''}: ${obj[k].message}`.trim());
|
||||
} else if (typeof obj[k] === 'string') {
|
||||
messages.push(obj[k]);
|
||||
} else {
|
||||
messages = messages.concat(this.flattenErrors(obj[k], newKey));
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DiscordAPIError;
|
||||
56
node_modules/discord.js/src/client/rest/RESTManager.js
generated
vendored
Normal file
56
node_modules/discord.js/src/client/rest/RESTManager.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
const UserAgentManager = require('./UserAgentManager');
|
||||
const RESTMethods = require('./RESTMethods');
|
||||
const SequentialRequestHandler = require('./RequestHandlers/Sequential');
|
||||
const BurstRequestHandler = require('./RequestHandlers/Burst');
|
||||
const APIRequest = require('./APIRequest');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class RESTManager {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
this.handlers = {};
|
||||
this.userAgentManager = new UserAgentManager(this);
|
||||
this.methods = new RESTMethods(this);
|
||||
this.rateLimitedEndpoints = {};
|
||||
this.globallyRateLimited = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (const handler of Object.values(this.handlers)) {
|
||||
if (handler.destroy) handler.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
push(handler, apiRequest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
handler.push({
|
||||
request: apiRequest,
|
||||
resolve,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRequestHandler() {
|
||||
switch (this.client.options.apiRequestMethod) {
|
||||
case 'sequential':
|
||||
return SequentialRequestHandler;
|
||||
case 'burst':
|
||||
return BurstRequestHandler;
|
||||
default:
|
||||
throw new Error(Constants.Errors.INVALID_RATE_LIMIT_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
makeRequest(method, url, auth, data, file, reason) {
|
||||
const apiRequest = new APIRequest(this, method, url, auth, data, file, reason);
|
||||
if (!this.handlers[apiRequest.route]) {
|
||||
const RequestHandlerType = this.getRequestHandler();
|
||||
this.handlers[apiRequest.route] = new RequestHandlerType(this, apiRequest.route);
|
||||
}
|
||||
|
||||
return this.push(this.handlers[apiRequest.route], apiRequest);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTManager;
|
||||
964
node_modules/discord.js/src/client/rest/RESTMethods.js
generated
vendored
Normal file
964
node_modules/discord.js/src/client/rest/RESTMethods.js
generated
vendored
Normal file
@@ -0,0 +1,964 @@
|
||||
const querystring = require('querystring');
|
||||
const long = require('long');
|
||||
const Permissions = require('../../util/Permissions');
|
||||
const Constants = require('../../util/Constants');
|
||||
const Endpoints = Constants.Endpoints;
|
||||
const Collection = require('../../util/Collection');
|
||||
const Util = require('../../util/Util');
|
||||
|
||||
const User = require('../../structures/User');
|
||||
const GuildMember = require('../../structures/GuildMember');
|
||||
const Message = require('../../structures/Message');
|
||||
const Role = require('../../structures/Role');
|
||||
const Invite = require('../../structures/Invite');
|
||||
const Webhook = require('../../structures/Webhook');
|
||||
const UserProfile = require('../../structures/UserProfile');
|
||||
const OAuth2Application = require('../../structures/OAuth2Application');
|
||||
const Channel = require('../../structures/Channel');
|
||||
const GroupDMChannel = require('../../structures/GroupDMChannel');
|
||||
const Guild = require('../../structures/Guild');
|
||||
const VoiceRegion = require('../../structures/VoiceRegion');
|
||||
const GuildAuditLogs = require('../../structures/GuildAuditLogs');
|
||||
|
||||
class RESTMethods {
|
||||
constructor(restManager) {
|
||||
this.rest = restManager;
|
||||
this.client = restManager.client;
|
||||
this._ackToken = null;
|
||||
}
|
||||
|
||||
login(token = this.client.token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof token !== 'string') throw new Error(Constants.Errors.INVALID_TOKEN);
|
||||
token = token.replace(/^Bot\s*/i, '');
|
||||
this.client.manager.connectToWebSocket(token, resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
return this.rest.makeRequest('post', Endpoints.logout, true, {});
|
||||
}
|
||||
|
||||
getGateway(bot = false) {
|
||||
return this.rest.makeRequest('get', bot ? Endpoints.gateway.bot : Endpoints.gateway, true);
|
||||
}
|
||||
|
||||
fetchVoiceRegions(guildID) {
|
||||
let endpoint;
|
||||
if (guildID) endpoint = Endpoints.Guild(guildID).voiceRegions;
|
||||
else endpoint = Endpoints.voiceRegions;
|
||||
return this.rest.makeRequest('get', endpoint, true).then(res => {
|
||||
const regions = new Collection();
|
||||
for (const region of res) regions.set(region.id, new VoiceRegion(region));
|
||||
return regions;
|
||||
});
|
||||
}
|
||||
|
||||
sendMessage(channel, content, { tts, nonce, embed, disableEveryone, split, code, reply } = {}, files = null) {
|
||||
return new Promise((resolve, reject) => { // eslint-disable-line complexity
|
||||
if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
|
||||
|
||||
// The nonce has to be a uint64 :<
|
||||
if (typeof nonce !== 'undefined') {
|
||||
nonce = parseInt(nonce);
|
||||
if (isNaN(nonce) || nonce < 0) throw new RangeError('Message nonce must fit in an unsigned 64-bit integer.');
|
||||
}
|
||||
|
||||
if (content) {
|
||||
if (split && typeof split !== 'object') split = {};
|
||||
|
||||
// Wrap everything in a code block
|
||||
if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
|
||||
content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
|
||||
content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
|
||||
if (split) {
|
||||
split.prepend = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n`;
|
||||
split.append = '\n```';
|
||||
}
|
||||
}
|
||||
|
||||
// Add zero-width spaces to @everyone/@here
|
||||
if (disableEveryone || (typeof disableEveryone === 'undefined' && this.client.options.disableEveryone)) {
|
||||
content = content.replace(/@(everyone|here)/g, '@\u200b$1');
|
||||
}
|
||||
|
||||
// Add the reply prefix
|
||||
if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') {
|
||||
const id = this.client.resolver.resolveUserID(reply);
|
||||
const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
|
||||
content = `${mention}${content ? `, ${content}` : ''}`;
|
||||
if (split) split.prepend = `${mention}, ${split.prepend || ''}`;
|
||||
}
|
||||
|
||||
// Split the content
|
||||
if (split) content = Util.splitMessage(content, split);
|
||||
} else if (reply && !(channel instanceof User || channel instanceof GuildMember) && channel.type !== 'dm') {
|
||||
const id = this.client.resolver.resolveUserID(reply);
|
||||
content = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
|
||||
}
|
||||
|
||||
const send = chan => {
|
||||
if (content instanceof Array) {
|
||||
const messages = [];
|
||||
(function sendChunk(list, index) {
|
||||
const options = index === list.length - 1 ? { tts, embed, files } : { tts };
|
||||
chan.send(list[index], options).then(message => {
|
||||
messages.push(message);
|
||||
if (index >= list.length - 1) return resolve(messages);
|
||||
return sendChunk(list, ++index);
|
||||
}).catch(reject);
|
||||
}(content, 0));
|
||||
} else {
|
||||
this.rest.makeRequest('post', Endpoints.Channel(chan).messages, true, {
|
||||
content, tts, nonce, embed,
|
||||
}, files).then(data => resolve(this.client.actions.MessageCreate.handle(data).message), reject);
|
||||
}
|
||||
};
|
||||
|
||||
if (channel instanceof User || channel instanceof GuildMember) this.createDM(channel).then(send, reject);
|
||||
else send(channel);
|
||||
});
|
||||
}
|
||||
|
||||
updateMessage(message, content, { embed, code, reply } = {}) {
|
||||
if (typeof content !== 'undefined') content = this.client.resolver.resolveString(content);
|
||||
|
||||
// Wrap everything in a code block
|
||||
if (typeof code !== 'undefined' && (typeof code !== 'boolean' || code === true)) {
|
||||
content = Util.escapeMarkdown(this.client.resolver.resolveString(content), true);
|
||||
content = `\`\`\`${typeof code !== 'boolean' ? code || '' : ''}\n${content}\n\`\`\``;
|
||||
}
|
||||
|
||||
// Add the reply prefix
|
||||
if (reply && message.channel.type !== 'dm') {
|
||||
const id = this.client.resolver.resolveUserID(reply);
|
||||
const mention = `<@${reply instanceof GuildMember && reply.nickname ? '!' : ''}${id}>`;
|
||||
content = `${mention}${content ? `, ${content}` : ''}`;
|
||||
}
|
||||
|
||||
return this.rest.makeRequest('patch', Endpoints.Message(message), true, {
|
||||
content, embed,
|
||||
}).then(data => this.client.actions.MessageUpdate.handle(data).updated);
|
||||
}
|
||||
|
||||
deleteMessage(message) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Message(message), true)
|
||||
.then(() =>
|
||||
this.client.actions.MessageDelete.handle({
|
||||
id: message.id,
|
||||
channel_id: message.channel.id,
|
||||
}).message
|
||||
);
|
||||
}
|
||||
|
||||
ackMessage(message) {
|
||||
return this.rest.makeRequest('post', Endpoints.Message(message).ack, true, { token: this._ackToken }).then(res => {
|
||||
if (res.token) this._ackToken = res.token;
|
||||
return message;
|
||||
});
|
||||
}
|
||||
|
||||
ackTextChannel(channel) {
|
||||
return this.rest.makeRequest('post', Endpoints.Channel(channel).Message(channel.lastMessageID).ack, true, {
|
||||
token: this._ackToken,
|
||||
}).then(res => {
|
||||
if (res.token) this._ackToken = res.token;
|
||||
return channel;
|
||||
});
|
||||
}
|
||||
|
||||
ackGuild(guild) {
|
||||
return this.rest.makeRequest('post', Endpoints.Guild(guild).ack, true).then(() => guild);
|
||||
}
|
||||
|
||||
bulkDeleteMessages(channel, messages) {
|
||||
return this.rest.makeRequest('post', Endpoints.Channel(channel).messages.bulkDelete, true, {
|
||||
messages: messages.map(m => m.id),
|
||||
}).then(() =>
|
||||
this.client.actions.MessageDeleteBulk.handle({
|
||||
channel_id: channel.id,
|
||||
messages,
|
||||
}).messages
|
||||
);
|
||||
}
|
||||
|
||||
search(target, options) {
|
||||
if (typeof options === 'string') options = { content: options };
|
||||
if (options.before) {
|
||||
if (!(options.before instanceof Date)) options.before = new Date(options.before);
|
||||
options.maxID = long.fromNumber(options.before.getTime() - 14200704e5).shiftLeft(22).toString();
|
||||
}
|
||||
if (options.after) {
|
||||
if (!(options.after instanceof Date)) options.after = new Date(options.after);
|
||||
options.minID = long.fromNumber(options.after.getTime() - 14200704e5).shiftLeft(22).toString();
|
||||
}
|
||||
if (options.during) {
|
||||
if (!(options.during instanceof Date)) options.during = new Date(options.during);
|
||||
const t = options.during.getTime() - 14200704e5;
|
||||
options.minID = long.fromNumber(t).shiftLeft(22).toString();
|
||||
options.maxID = long.fromNumber(t + 86400000).shiftLeft(22).toString();
|
||||
}
|
||||
if (options.channel) options.channel = this.client.resolver.resolveChannelID(options.channel);
|
||||
if (options.author) options.author = this.client.resolver.resolveUserID(options.author);
|
||||
if (options.mentions) options.mentions = this.client.resolver.resolveUserID(options.options.mentions);
|
||||
options = {
|
||||
content: options.content,
|
||||
max_id: options.maxID,
|
||||
min_id: options.minID,
|
||||
has: options.has,
|
||||
channel_id: options.channel,
|
||||
author_id: options.author,
|
||||
author_type: options.authorType,
|
||||
context_size: options.contextSize,
|
||||
sort_by: options.sortBy,
|
||||
sort_order: options.sortOrder,
|
||||
limit: options.limit,
|
||||
offset: options.offset,
|
||||
mentions: options.mentions,
|
||||
mentions_everyone: options.mentionsEveryone,
|
||||
link_hostname: options.linkHostname,
|
||||
embed_provider: options.embedProvider,
|
||||
embed_type: options.embedType,
|
||||
attachment_filename: options.attachmentFilename,
|
||||
attachment_extension: options.attachmentExtension,
|
||||
include_nsfw: options.nsfw,
|
||||
};
|
||||
|
||||
for (const key in options) if (options[key] === undefined) delete options[key];
|
||||
const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
|
||||
|
||||
let endpoint;
|
||||
if (target instanceof Channel) {
|
||||
endpoint = Endpoints.Channel(target).search;
|
||||
} else if (target instanceof Guild) {
|
||||
endpoint = Endpoints.Guild(target).search;
|
||||
} else {
|
||||
throw new TypeError('Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.');
|
||||
}
|
||||
return this.rest.makeRequest('get', `${endpoint}?${queryString}`, true).then(body => {
|
||||
const messages = body.messages.map(x =>
|
||||
x.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))
|
||||
);
|
||||
return {
|
||||
totalResults: body.total_results,
|
||||
messages,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
createChannel(guild, channelName, channelType, overwrites, reason) {
|
||||
if (overwrites instanceof Collection || overwrites instanceof Array) {
|
||||
overwrites = overwrites.map(overwrite => {
|
||||
let allow = overwrite.allow || overwrite._allowed;
|
||||
let deny = overwrite.deny || overwrite._denied;
|
||||
if (allow instanceof Array) allow = Permissions.resolve(allow);
|
||||
if (deny instanceof Array) deny = Permissions.resolve(deny);
|
||||
|
||||
const role = this.client.resolver.resolveRole(guild, overwrite.id);
|
||||
if (role) {
|
||||
overwrite.id = role.id;
|
||||
overwrite.type = 'role';
|
||||
} else {
|
||||
overwrite.id = this.client.resolver.resolveUserID(overwrite.id);
|
||||
overwrite.type = 'member';
|
||||
}
|
||||
|
||||
return {
|
||||
allow,
|
||||
deny,
|
||||
type: overwrite.type,
|
||||
id: overwrite.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
return this.rest.makeRequest('post', Endpoints.Guild(guild).channels, true, {
|
||||
name: channelName,
|
||||
type: channelType ? Constants.ChannelTypes[channelType.toUpperCase()] : 'text',
|
||||
permission_overwrites: overwrites,
|
||||
}, undefined, reason).then(data => this.client.actions.ChannelCreate.handle(data).channel);
|
||||
}
|
||||
|
||||
createDM(recipient) {
|
||||
const dmChannel = this.getExistingDM(recipient);
|
||||
if (dmChannel) return Promise.resolve(dmChannel);
|
||||
return this.rest.makeRequest('post', Endpoints.User(this.client.user).channels, true, {
|
||||
recipient_id: recipient.id,
|
||||
}).then(data => this.client.actions.ChannelCreate.handle(data).channel);
|
||||
}
|
||||
|
||||
createGroupDM(options) {
|
||||
const data = this.client.user.bot ?
|
||||
{ access_tokens: options.accessTokens, nicks: options.nicks } :
|
||||
{ recipients: options.recipients };
|
||||
return this.rest.makeRequest('post', Endpoints.User('@me').channels, true, data)
|
||||
.then(res => new GroupDMChannel(this.client, res));
|
||||
}
|
||||
|
||||
addUserToGroupDM(channel, options) {
|
||||
const data = this.client.user.bot ?
|
||||
{ nick: options.nick, access_token: options.accessToken } :
|
||||
{ recipient: options.id };
|
||||
return this.rest.makeRequest('put', Endpoints.Channel(channel).Recipient(options.id), true, data)
|
||||
.then(() => channel);
|
||||
}
|
||||
|
||||
removeUserFromGroupDM(channel, userId) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Channel(channel).Recipient(userId), true)
|
||||
.then(() => channel);
|
||||
}
|
||||
|
||||
updateGroupDMChannel(channel, _data) {
|
||||
const data = {};
|
||||
data.name = _data.name;
|
||||
data.icon = _data.icon;
|
||||
return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data).then(() => channel);
|
||||
}
|
||||
|
||||
getExistingDM(recipient) {
|
||||
return this.client.channels.find(channel =>
|
||||
channel.recipient && channel.recipient.id === recipient.id
|
||||
);
|
||||
}
|
||||
|
||||
deleteChannel(channel, reason) {
|
||||
if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel);
|
||||
if (!channel) return Promise.reject(new Error('No channel to delete.'));
|
||||
return this.rest.makeRequest('delete', Endpoints.Channel(channel), true, undefined, undefined, reason)
|
||||
.then(data => {
|
||||
data.id = channel.id;
|
||||
return this.client.actions.ChannelDelete.handle(data).channel;
|
||||
});
|
||||
}
|
||||
|
||||
updateChannel(channel, _data, reason) {
|
||||
const data = {};
|
||||
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 ? channel.bitrate * 1000 : undefined);
|
||||
data.user_limit = typeof _data.userLimit !== 'undefined' ? _data.userLimit : channel.userLimit;
|
||||
data.parent_id = _data.parent || (channel.parent ? channel.parent.id : undefined);
|
||||
return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data, undefined, reason).then(newData =>
|
||||
this.client.actions.ChannelUpdate.handle(newData).updated
|
||||
);
|
||||
}
|
||||
|
||||
leaveGuild(guild) {
|
||||
if (guild.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.'));
|
||||
return this.rest.makeRequest('delete', Endpoints.User('@me').Guild(guild.id), true).then(() =>
|
||||
this.client.actions.GuildDelete.handle({ id: guild.id }).guild
|
||||
);
|
||||
}
|
||||
|
||||
createGuild(options) {
|
||||
options.icon = this.client.resolver.resolveBase64(options.icon) || null;
|
||||
options.region = options.region || 'us-central';
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('post', Endpoints.guilds, true, options).then(data => {
|
||||
if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id));
|
||||
|
||||
const handleGuild = guild => {
|
||||
if (guild.id === data.id) {
|
||||
this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild);
|
||||
this.client.clearTimeout(timeout);
|
||||
resolve(guild);
|
||||
}
|
||||
};
|
||||
this.client.on(Constants.Events.GUILD_CREATE, handleGuild);
|
||||
|
||||
const timeout = this.client.setTimeout(() => {
|
||||
this.client.removeListener(Constants.Events.GUILD_CREATE, handleGuild);
|
||||
reject(new Error('Took too long to receive guild data.'));
|
||||
}, 10000);
|
||||
return undefined;
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
// Untested but probably will work
|
||||
deleteGuild(guild) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Guild(guild), true).then(() =>
|
||||
this.client.actions.GuildDelete.handle({ id: guild.id }).guild
|
||||
);
|
||||
}
|
||||
|
||||
getUser(userID, cache) {
|
||||
return this.rest.makeRequest('get', Endpoints.User(userID), true).then(data => {
|
||||
if (cache) return this.client.actions.UserGet.handle(data).user;
|
||||
else return new User(this.client, data);
|
||||
});
|
||||
}
|
||||
|
||||
updateCurrentUser(_data, password) {
|
||||
const user = this.client.user;
|
||||
const data = {};
|
||||
data.username = _data.username || user.username;
|
||||
data.avatar = typeof _data.avatar === 'undefined' ? user.avatar : this.client.resolver.resolveBase64(_data.avatar);
|
||||
if (!user.bot) {
|
||||
data.email = _data.email || user.email;
|
||||
data.password = password;
|
||||
if (_data.new_password) data.new_password = _data.newPassword;
|
||||
}
|
||||
return this.rest.makeRequest('patch', Endpoints.User('@me'), true, data).then(newData =>
|
||||
this.client.actions.UserUpdate.handle(newData).updated
|
||||
);
|
||||
}
|
||||
|
||||
updateGuild(guild, data, reason) {
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(guild), true, data, undefined, reason).then(newData =>
|
||||
this.client.actions.GuildUpdate.handle(newData).updated
|
||||
);
|
||||
}
|
||||
|
||||
kickGuildMember(guild, member, reason) {
|
||||
return this.rest.makeRequest(
|
||||
'delete', Endpoints.Guild(guild).Member(member), true,
|
||||
undefined, undefined, reason)
|
||||
.then(() =>
|
||||
this.client.actions.GuildMemberRemove.handle({
|
||||
guild_id: guild.id,
|
||||
user: member.user,
|
||||
}).member
|
||||
);
|
||||
}
|
||||
|
||||
createGuildRole(guild, data, reason) {
|
||||
if (data.color) data.color = this.client.resolver.resolveColor(data.color);
|
||||
if (data.permissions) data.permissions = Permissions.resolve(data.permissions);
|
||||
return this.rest.makeRequest('post', Endpoints.Guild(guild).roles, true, data, undefined, reason).then(r => {
|
||||
const { role } = this.client.actions.GuildRoleCreate.handle({
|
||||
guild_id: guild.id,
|
||||
role: r,
|
||||
});
|
||||
if (data.position) return role.setPosition(data.position, reason);
|
||||
return role;
|
||||
});
|
||||
}
|
||||
|
||||
deleteGuildRole(role, reason) {
|
||||
return this.rest.makeRequest(
|
||||
'delete', Endpoints.Guild(role.guild).Role(role.id), true,
|
||||
undefined, undefined, reason)
|
||||
.then(() =>
|
||||
this.client.actions.GuildRoleDelete.handle({
|
||||
guild_id: role.guild.id,
|
||||
role_id: role.id,
|
||||
}).role
|
||||
);
|
||||
}
|
||||
|
||||
setChannelOverwrite(channel, payload) {
|
||||
return this.rest.makeRequest('put', `${Endpoints.Channel(channel).permissions}/${payload.id}`, true, payload);
|
||||
}
|
||||
|
||||
deletePermissionOverwrites(overwrite, reason) {
|
||||
return this.rest.makeRequest(
|
||||
'delete', `${Endpoints.Channel(overwrite.channel).permissions}/${overwrite.id}`,
|
||||
true, undefined, undefined, reason
|
||||
).then(() => overwrite);
|
||||
}
|
||||
|
||||
getChannelMessages(channel, payload = {}) {
|
||||
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}`);
|
||||
|
||||
let endpoint = Endpoints.Channel(channel).messages;
|
||||
if (params.length > 0) endpoint += `?${params.join('&')}`;
|
||||
return this.rest.makeRequest('get', endpoint, true);
|
||||
}
|
||||
|
||||
getChannelMessage(channel, messageID) {
|
||||
const msg = channel.messages.get(messageID);
|
||||
if (msg) return Promise.resolve(msg);
|
||||
return this.rest.makeRequest('get', Endpoints.Channel(channel).Message(messageID), true);
|
||||
}
|
||||
|
||||
putGuildMember(guild, user, options) {
|
||||
options.access_token = options.accessToken;
|
||||
if (options.roles) {
|
||||
const roles = options.roles;
|
||||
if (roles instanceof Collection || (roles instanceof Array && roles[0] instanceof Role)) {
|
||||
options.roles = roles.map(role => role.id);
|
||||
}
|
||||
}
|
||||
return this.rest.makeRequest('put', Endpoints.Guild(guild).Member(user.id), true, options)
|
||||
.then(data => this.client.actions.GuildMemberGet.handle(guild, data).member);
|
||||
}
|
||||
|
||||
getGuildMember(guild, user, cache) {
|
||||
return this.rest.makeRequest('get', Endpoints.Guild(guild).Member(user.id), true).then(data => {
|
||||
if (cache) return this.client.actions.GuildMemberGet.handle(guild, data).member;
|
||||
else return new GuildMember(guild, data);
|
||||
});
|
||||
}
|
||||
|
||||
updateGuildMember(member, data, reason) {
|
||||
if (data.channel) {
|
||||
data.channel_id = this.client.resolver.resolveChannel(data.channel).id;
|
||||
data.channel = null;
|
||||
}
|
||||
if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role);
|
||||
|
||||
let endpoint = Endpoints.Member(member);
|
||||
// Fix your endpoints, discord ;-;
|
||||
if (member.id === this.client.user.id) {
|
||||
const keys = Object.keys(data);
|
||||
if (keys.length === 1 && keys[0] === 'nick') {
|
||||
endpoint = Endpoints.Member(member).nickname;
|
||||
}
|
||||
}
|
||||
|
||||
return this.rest.makeRequest('patch', endpoint, true, data, undefined, reason).then(newData =>
|
||||
member.guild._updateMember(member, newData).mem
|
||||
);
|
||||
}
|
||||
|
||||
addMemberRole(member, role, reason) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (member._roles.includes(role.id)) return resolve(member);
|
||||
|
||||
const listener = (oldMember, newMember) => {
|
||||
if (!oldMember._roles.includes(role.id) && newMember._roles.includes(role.id)) {
|
||||
this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
|
||||
resolve(newMember);
|
||||
}
|
||||
};
|
||||
|
||||
this.client.on(Constants.Events.GUILD_MEMBER_UPDATE, listener);
|
||||
const timeout = this.client.setTimeout(() =>
|
||||
this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener), 10e3);
|
||||
|
||||
return this.rest.makeRequest('put', Endpoints.Member(member).Role(role.id), true, undefined, undefined, reason)
|
||||
.catch(err => {
|
||||
this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
this.client.clearTimeout(timeout);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
removeMemberRole(member, role, reason) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!member._roles.includes(role.id)) return resolve(member);
|
||||
|
||||
const listener = (oldMember, newMember) => {
|
||||
if (oldMember._roles.includes(role.id) && !newMember._roles.includes(role.id)) {
|
||||
this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener);
|
||||
resolve(newMember);
|
||||
}
|
||||
};
|
||||
|
||||
this.client.on(Constants.Events.GUILD_MEMBER_UPDATE, listener);
|
||||
const timeout = this.client.setTimeout(() =>
|
||||
this.client.removeListener(Constants.Events.GUILD_MEMBER_UPDATE, listener), 10e3);
|
||||
|
||||
return this.rest.makeRequest('delete', Endpoints.Member(member).Role(role.id), true, undefined, undefined, reason)
|
||||
.catch(err => {
|
||||
this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
this.client.clearTimeout(timeout);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sendTyping(channelID) {
|
||||
return this.rest.makeRequest('post', Endpoints.Channel(channelID).typing, true);
|
||||
}
|
||||
|
||||
banGuildMember(guild, member, options) {
|
||||
const id = this.client.resolver.resolveUserID(member);
|
||||
if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.'));
|
||||
|
||||
const url = `${Endpoints.Guild(guild).bans}/${id}?${querystring.stringify(options)}`;
|
||||
return this.rest.makeRequest('put', url, true).then(() => {
|
||||
if (member instanceof GuildMember) return member;
|
||||
const user = this.client.resolver.resolveUser(id);
|
||||
if (user) {
|
||||
member = this.client.resolver.resolveGuildMember(guild, user);
|
||||
return member || user;
|
||||
}
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
||||
unbanGuildMember(guild, member, reason) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.client.resolver.resolveUserID(member);
|
||||
if (!id) throw new Error('Couldn\'t resolve the user ID to unban.');
|
||||
|
||||
const listener = (eGuild, eUser) => {
|
||||
if (eGuild.id === guild.id && eUser.id === id) {
|
||||
this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
this.client.clearTimeout(timeout);
|
||||
resolve(eUser);
|
||||
}
|
||||
};
|
||||
this.client.on(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
|
||||
const timeout = this.client.setTimeout(() => {
|
||||
this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
reject(new Error('Took too long to receive the ban remove event.'));
|
||||
}, 10000);
|
||||
|
||||
this.rest.makeRequest('delete', `${Endpoints.Guild(guild).bans}/${id}`, true, undefined, undefined, reason)
|
||||
.catch(err => {
|
||||
this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener);
|
||||
this.client.clearTimeout(timeout);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getGuildBans(guild) {
|
||||
return this.rest.makeRequest('get', Endpoints.Guild(guild).bans, true).then(bans =>
|
||||
bans.reduce((collection, ban) => {
|
||||
collection.set(ban.user.id, {
|
||||
reason: ban.reason,
|
||||
user: this.client.dataManager.newUser(ban.user),
|
||||
});
|
||||
return collection;
|
||||
}, new Collection())
|
||||
);
|
||||
}
|
||||
|
||||
updateGuildRole(role, _data, reason) {
|
||||
const data = {};
|
||||
data.name = _data.name || role.name;
|
||||
data.position = typeof _data.position !== 'undefined' ? _data.position : role.position;
|
||||
data.color = this.client.resolver.resolveColor(_data.color || role.color);
|
||||
data.hoist = typeof _data.hoist !== 'undefined' ? _data.hoist : role.hoist;
|
||||
data.mentionable = typeof _data.mentionable !== 'undefined' ? _data.mentionable : role.mentionable;
|
||||
|
||||
if (_data.permissions) data.permissions = Permissions.resolve(_data.permissions);
|
||||
else data.permissions = role.permissions;
|
||||
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(role.guild).Role(role.id), true, data, undefined, reason)
|
||||
.then(_role =>
|
||||
this.client.actions.GuildRoleUpdate.handle({
|
||||
role: _role,
|
||||
guild_id: role.guild.id,
|
||||
}).updated
|
||||
);
|
||||
}
|
||||
|
||||
pinMessage(message) {
|
||||
return this.rest.makeRequest('put', Endpoints.Channel(message.channel).Pin(message.id), true)
|
||||
.then(() => message);
|
||||
}
|
||||
|
||||
unpinMessage(message) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Channel(message.channel).Pin(message.id), true)
|
||||
.then(() => message);
|
||||
}
|
||||
|
||||
getChannelPinnedMessages(channel) {
|
||||
return this.rest.makeRequest('get', Endpoints.Channel(channel).pins, true);
|
||||
}
|
||||
|
||||
createChannelInvite(channel, options, reason) {
|
||||
const payload = {};
|
||||
payload.temporary = options.temporary;
|
||||
payload.max_age = options.maxAge;
|
||||
payload.max_uses = options.maxUses;
|
||||
payload.unique = options.unique;
|
||||
return this.rest.makeRequest('post', Endpoints.Channel(channel).invites, true, payload, undefined, reason)
|
||||
.then(invite => new Invite(this.client, invite));
|
||||
}
|
||||
|
||||
deleteInvite(invite, reason) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Invite(invite.code), true, undefined, undefined, reason)
|
||||
.then(() => invite);
|
||||
}
|
||||
|
||||
getInvite(code) {
|
||||
return this.rest.makeRequest('get', Endpoints.Invite(code), true).then(invite =>
|
||||
new Invite(this.client, invite)
|
||||
);
|
||||
}
|
||||
|
||||
getGuildInvites(guild) {
|
||||
return this.rest.makeRequest('get', Endpoints.Guild(guild).invites, true).then(inviteItems => {
|
||||
const invites = new Collection();
|
||||
for (const inviteItem of inviteItems) {
|
||||
const invite = new Invite(this.client, inviteItem);
|
||||
invites.set(invite.code, invite);
|
||||
}
|
||||
return invites;
|
||||
});
|
||||
}
|
||||
|
||||
pruneGuildMembers(guild, days, dry, reason) {
|
||||
return this.rest.makeRequest(dry ?
|
||||
'get' :
|
||||
'post',
|
||||
`${Endpoints.Guild(guild).prune}?days=${days}`, true, undefined, undefined, reason)
|
||||
.then(data => data.pruned);
|
||||
}
|
||||
|
||||
createEmoji(guild, image, name, roles, reason) {
|
||||
const data = { image, name };
|
||||
if (roles) data.roles = roles.map(r => r.id ? r.id : r);
|
||||
return this.rest.makeRequest('post', Endpoints.Guild(guild).emojis, true, data, undefined, reason)
|
||||
.then(emoji => this.client.actions.GuildEmojiCreate.handle(guild, emoji).emoji);
|
||||
}
|
||||
|
||||
updateEmoji(emoji, _data, reason) {
|
||||
const data = {};
|
||||
if (_data.name) data.name = _data.name;
|
||||
if (_data.roles) data.roles = _data.roles.map(r => r.id ? r.id : r);
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true, data, undefined, reason)
|
||||
.then(newEmoji => this.client.actions.GuildEmojiUpdate.handle(emoji, newEmoji).emoji);
|
||||
}
|
||||
|
||||
deleteEmoji(emoji, reason) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true, undefined, reason)
|
||||
.then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data);
|
||||
}
|
||||
|
||||
getGuildAuditLogs(guild, options = {}) {
|
||||
if (options.before && options.before instanceof GuildAuditLogs.Entry) options.before = options.before.id;
|
||||
if (options.after && options.after instanceof GuildAuditLogs.Entry) options.after = options.after.id;
|
||||
if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type];
|
||||
|
||||
const queryString = (querystring.stringify({
|
||||
before: options.before,
|
||||
after: options.after,
|
||||
limit: options.limit,
|
||||
user_id: this.client.resolver.resolveUserID(options.user),
|
||||
action_type: options.type,
|
||||
}).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
|
||||
|
||||
return this.rest.makeRequest('get', `${Endpoints.Guild(guild).auditLogs}?${queryString}`, true)
|
||||
.then(data => GuildAuditLogs.build(guild, data));
|
||||
}
|
||||
|
||||
getWebhook(id, token) {
|
||||
return this.rest.makeRequest('get', Endpoints.Webhook(id, token), !token).then(data =>
|
||||
new Webhook(this.client, data)
|
||||
);
|
||||
}
|
||||
|
||||
getGuildWebhooks(guild) {
|
||||
return this.rest.makeRequest('get', Endpoints.Guild(guild).webhooks, true).then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
});
|
||||
}
|
||||
|
||||
getChannelWebhooks(channel) {
|
||||
return this.rest.makeRequest('get', Endpoints.Channel(channel).webhooks, true).then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
|
||||
return hooks;
|
||||
});
|
||||
}
|
||||
|
||||
createWebhook(channel, name, avatar, reason) {
|
||||
return this.rest.makeRequest('post', Endpoints.Channel(channel).webhooks, true, { name, avatar }, undefined, reason)
|
||||
.then(data => new Webhook(this.client, data));
|
||||
}
|
||||
|
||||
editWebhook(webhook, name, avatar) {
|
||||
return this.rest.makeRequest('patch', Endpoints.Webhook(webhook.id, webhook.token), false, {
|
||||
name,
|
||||
avatar,
|
||||
}).then(data => {
|
||||
webhook.name = data.name;
|
||||
webhook.avatar = data.avatar;
|
||||
return webhook;
|
||||
});
|
||||
}
|
||||
|
||||
deleteWebhook(webhook, reason) {
|
||||
return this.rest.makeRequest(
|
||||
'delete', Endpoints.Webhook(webhook.id, webhook.token),
|
||||
false, undefined, undefined, reason);
|
||||
}
|
||||
|
||||
sendWebhookMessage(webhook, content, { avatarURL, tts, embeds, username } = {}, files = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
username = username || webhook.name;
|
||||
|
||||
if (content instanceof Array) {
|
||||
const messages = [];
|
||||
(function sendChunk(list, index) {
|
||||
const options = index === list.length - 1 ? { tts, embeds, files } : { tts };
|
||||
webhook.send(list[index], options).then(message => {
|
||||
messages.push(message);
|
||||
if (index >= list.length - 1) return resolve(messages);
|
||||
return sendChunk(list, ++index);
|
||||
}).catch(reject);
|
||||
}(content, 0));
|
||||
} else {
|
||||
this.rest.makeRequest('post', `${Endpoints.Webhook(webhook.id, webhook.token)}?wait=true`, false, {
|
||||
username,
|
||||
avatar_url: avatarURL,
|
||||
content,
|
||||
tts,
|
||||
embeds,
|
||||
}, files).then(data => {
|
||||
if (!this.client.channels) resolve(data);
|
||||
else resolve(this.client.actions.MessageCreate.handle(data).message);
|
||||
}, reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sendSlackWebhookMessage(webhook, body) {
|
||||
return this.rest.makeRequest(
|
||||
'post', `${Endpoints.Webhook(webhook.id, webhook.token)}/slack?wait=true`, false, body
|
||||
);
|
||||
}
|
||||
|
||||
fetchUserProfile(user) {
|
||||
return this.rest.makeRequest('get', Endpoints.User(user).profile, true).then(data =>
|
||||
new UserProfile(user, data)
|
||||
);
|
||||
}
|
||||
|
||||
fetchMentions(options) {
|
||||
if (options.guild instanceof Guild) options.guild = options.guild.id;
|
||||
Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options);
|
||||
|
||||
return this.rest.makeRequest(
|
||||
'get', Endpoints.User('@me').Mentions(options.limit, options.roles, options.everyone, options.guild), true
|
||||
).then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client)));
|
||||
}
|
||||
|
||||
addFriend(user) {
|
||||
return this.rest.makeRequest('post', Endpoints.User('@me'), true, {
|
||||
username: user.username,
|
||||
discriminator: user.discriminator,
|
||||
}).then(() => user);
|
||||
}
|
||||
|
||||
removeFriend(user) {
|
||||
return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true)
|
||||
.then(() => user);
|
||||
}
|
||||
|
||||
blockUser(user) {
|
||||
return this.rest.makeRequest('put', Endpoints.User('@me').Relationship(user.id), true, { type: 2 })
|
||||
.then(() => user);
|
||||
}
|
||||
|
||||
unblockUser(user) {
|
||||
return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true)
|
||||
.then(() => user);
|
||||
}
|
||||
|
||||
updateChannelPositions(guildID, channels) {
|
||||
const data = new Array(channels.length);
|
||||
for (let i = 0; i < channels.length; i++) {
|
||||
data[i] = {
|
||||
id: this.client.resolver.resolveChannelID(channels[i].channel),
|
||||
position: channels[i].position,
|
||||
};
|
||||
}
|
||||
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(guildID).channels, true, data).then(() =>
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: guildID,
|
||||
channels,
|
||||
}).guild
|
||||
);
|
||||
}
|
||||
|
||||
setRolePositions(guildID, roles) {
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(guildID).roles, true, roles).then(() =>
|
||||
this.client.actions.GuildRolesPositionUpdate.handle({
|
||||
guild_id: guildID,
|
||||
roles,
|
||||
}).guild
|
||||
);
|
||||
}
|
||||
|
||||
setChannelPositions(guildID, channels) {
|
||||
return this.rest.makeRequest('patch', Endpoints.Guild(guildID).channels, true, channels).then(() =>
|
||||
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||
guild_id: guildID,
|
||||
channels,
|
||||
}).guild
|
||||
);
|
||||
}
|
||||
|
||||
addMessageReaction(message, emoji) {
|
||||
return this.rest.makeRequest(
|
||||
'put', Endpoints.Message(message).Reaction(emoji).User('@me'), true
|
||||
).then(() =>
|
||||
message._addReaction(Util.parseEmoji(emoji), message.client.user)
|
||||
);
|
||||
}
|
||||
|
||||
removeMessageReaction(message, emoji, userID) {
|
||||
const endpoint = Endpoints.Message(message).Reaction(emoji).User(userID === this.client.user.id ? '@me' : userID);
|
||||
return this.rest.makeRequest('delete', endpoint, true).then(() =>
|
||||
this.client.actions.MessageReactionRemove.handle({
|
||||
user_id: userID,
|
||||
message_id: message.id,
|
||||
emoji: Util.parseEmoji(emoji),
|
||||
channel_id: message.channel.id,
|
||||
}).reaction
|
||||
);
|
||||
}
|
||||
|
||||
removeMessageReactions(message) {
|
||||
return this.rest.makeRequest('delete', Endpoints.Message(message).reactions, true)
|
||||
.then(() => message);
|
||||
}
|
||||
|
||||
getMessageReactionUsers(message, emoji, options) {
|
||||
const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&');
|
||||
|
||||
return this.rest.makeRequest('get', `${Endpoints.Message(message).Reaction(emoji)}?${queryString}`, true);
|
||||
}
|
||||
|
||||
getApplication(id) {
|
||||
return this.rest.makeRequest('get', Endpoints.OAUTH2.Application(id), true).then(app =>
|
||||
new OAuth2Application(this.client, app)
|
||||
);
|
||||
}
|
||||
|
||||
resetApplication(id) {
|
||||
return this.rest.makeRequest('post', Endpoints.OAUTH2.Application(id).resetToken, true)
|
||||
.then(() => this.rest.makeRequest('post', Endpoints.OAUTH2.Application(id).resetSecret, true))
|
||||
.then(app => new OAuth2Application(this.client, app));
|
||||
}
|
||||
|
||||
setNote(user, note) {
|
||||
return this.rest.makeRequest('put', Endpoints.User(user).note, true, { note }).then(() => user);
|
||||
}
|
||||
|
||||
acceptInvite(code) {
|
||||
if (code.id) code = code.id;
|
||||
return new Promise((resolve, reject) =>
|
||||
this.rest.makeRequest('post', Endpoints.Invite(code), true).then(res => {
|
||||
const handler = guild => {
|
||||
if (guild.id === res.id) {
|
||||
resolve(guild);
|
||||
this.client.removeListener(Constants.Events.GUILD_CREATE, handler);
|
||||
}
|
||||
};
|
||||
this.client.on(Constants.Events.GUILD_CREATE, handler);
|
||||
this.client.setTimeout(() => {
|
||||
this.client.removeListener(Constants.Events.GUILD_CREATE, handler);
|
||||
reject(new Error('Accepting invite timed out'));
|
||||
}, 120e3);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
patchUserSettings(data) {
|
||||
return this.rest.makeRequest('patch', Constants.Endpoints.User('@me').settings, true, data);
|
||||
}
|
||||
|
||||
patchClientUserGuildSettings(guildID, data) {
|
||||
return this.rest.makeRequest('patch', Constants.Endpoints.User('@me').Guild(guildID).settings, true, data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTMethods;
|
||||
71
node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
generated
vendored
Normal file
71
node_modules/discord.js/src/client/rest/RequestHandlers/Burst.js
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
const RequestHandler = require('./RequestHandler');
|
||||
const DiscordAPIError = require('../DiscordAPIError');
|
||||
|
||||
class BurstRequestHandler extends RequestHandler {
|
||||
constructor(restManager, endpoint) {
|
||||
super(restManager, endpoint);
|
||||
|
||||
this.client = restManager.client;
|
||||
|
||||
this.limit = Infinity;
|
||||
this.resetTime = null;
|
||||
this.remaining = 1;
|
||||
this.timeDifference = 0;
|
||||
|
||||
this.resetTimeout = null;
|
||||
}
|
||||
|
||||
push(request) {
|
||||
super.push(request);
|
||||
this.handle();
|
||||
}
|
||||
|
||||
execute(item) {
|
||||
if (!item) return;
|
||||
item.request.gen().end((err, res) => {
|
||||
if (res && res.headers) {
|
||||
this.limit = Number(res.headers['x-ratelimit-limit']);
|
||||
this.resetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
|
||||
this.remaining = Number(res.headers['x-ratelimit-remaining']);
|
||||
this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
|
||||
}
|
||||
if (err) {
|
||||
if (err.status === 429) {
|
||||
this.queue.unshift(item);
|
||||
if (res.headers['x-ratelimit-global']) this.globalLimit = true;
|
||||
if (this.resetTimeout) return;
|
||||
this.resetTimeout = this.client.setTimeout(() => {
|
||||
this.remaining = this.limit;
|
||||
this.globalLimit = false;
|
||||
this.handle();
|
||||
this.resetTimeout = null;
|
||||
}, Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
|
||||
} else if (err.status >= 500 && err.status < 600) {
|
||||
this.queue.unshift(item);
|
||||
this.resetTimeout = this.client.setTimeout(() => {
|
||||
this.handle();
|
||||
this.resetTimeout = null;
|
||||
}, 1e3 + this.client.options.restTimeOffset);
|
||||
} else {
|
||||
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
|
||||
this.handle();
|
||||
}
|
||||
} else {
|
||||
this.globalLimit = false;
|
||||
const data = res && res.body ? res.body : {};
|
||||
item.resolve(data);
|
||||
this.handle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handle() {
|
||||
super.handle();
|
||||
if (this.remaining <= 0 || this.queue.length === 0 || this.globalLimit) return;
|
||||
this.execute(this.queue.shift());
|
||||
this.remaining--;
|
||||
this.handle();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BurstRequestHandler;
|
||||
54
node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
generated
vendored
Normal file
54
node_modules/discord.js/src/client/rest/RequestHandlers/RequestHandler.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* A base class for different types of rate limiting handlers for the REST API.
|
||||
* @private
|
||||
*/
|
||||
class RequestHandler {
|
||||
/**
|
||||
* @param {RESTManager} restManager The REST manager to use
|
||||
*/
|
||||
constructor(restManager) {
|
||||
/**
|
||||
* The RESTManager that instantiated this RequestHandler
|
||||
* @type {RESTManager}
|
||||
*/
|
||||
this.restManager = restManager;
|
||||
|
||||
/**
|
||||
* A list of requests that have yet to be processed
|
||||
* @type {APIRequest[]}
|
||||
*/
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the client is being rate limited on every endpoint
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get globalLimit() {
|
||||
return this.restManager.globallyRateLimited;
|
||||
}
|
||||
|
||||
set globalLimit(value) {
|
||||
this.restManager.globallyRateLimited = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new API request into this bucket.
|
||||
* @param {APIRequest} request The new request to push into the queue
|
||||
*/
|
||||
push(request) {
|
||||
this.queue.push(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get this RequestHandler to process its current queue.
|
||||
*/
|
||||
handle() {} // eslint-disable-line no-empty-function
|
||||
|
||||
destroy() {
|
||||
this.queue = [];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequestHandler;
|
||||
101
node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
generated
vendored
Normal file
101
node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
const RequestHandler = require('./RequestHandler');
|
||||
const DiscordAPIError = require('../DiscordAPIError');
|
||||
|
||||
/**
|
||||
* Handles API Requests sequentially, i.e. we wait until the current request is finished before moving onto
|
||||
* the next. This plays a _lot_ nicer in terms of avoiding 429's when there is more than one session of the account,
|
||||
* but it can be slower.
|
||||
* @extends {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);
|
||||
|
||||
/**
|
||||
* The endpoint that this handler is handling
|
||||
* @type {string}
|
||||
*/
|
||||
this.endpoint = endpoint;
|
||||
|
||||
/**
|
||||
* The time difference between Discord's Dates and the local computer's Dates. A positive number means the local
|
||||
* computer's time is ahead of Discord's
|
||||
* @type {number}
|
||||
*/
|
||||
this.timeDifference = 0;
|
||||
|
||||
/**
|
||||
* Whether the queue is being processed or not
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.busy = false;
|
||||
}
|
||||
|
||||
push(request) {
|
||||
super.push(request);
|
||||
this.handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>}
|
||||
*/
|
||||
execute(item) {
|
||||
this.busy = true;
|
||||
return new Promise(resolve => {
|
||||
item.request.gen().end((err, res) => {
|
||||
if (res && res.headers) {
|
||||
this.requestLimit = Number(res.headers['x-ratelimit-limit']);
|
||||
this.requestResetTime = Number(res.headers['x-ratelimit-reset']) * 1000;
|
||||
this.requestRemaining = Number(res.headers['x-ratelimit-remaining']);
|
||||
this.timeDifference = Date.now() - new Date(res.headers.date).getTime();
|
||||
}
|
||||
if (err) {
|
||||
if (err.status === 429) {
|
||||
this.queue.unshift(item);
|
||||
this.restManager.client.setTimeout(() => {
|
||||
this.globalLimit = false;
|
||||
resolve();
|
||||
}, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset);
|
||||
if (res.headers['x-ratelimit-global']) this.globalLimit = true;
|
||||
} else if (err.status >= 500 && err.status < 600) {
|
||||
this.queue.unshift(item);
|
||||
this.restManager.client.setTimeout(resolve, 1e3 + this.restManager.client.options.restTimeOffset);
|
||||
} else {
|
||||
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
|
||||
resolve(err);
|
||||
}
|
||||
} else {
|
||||
this.globalLimit = false;
|
||||
const data = res && res.body ? res.body : {};
|
||||
item.resolve(data);
|
||||
if (this.requestRemaining === 0) {
|
||||
this.restManager.client.setTimeout(
|
||||
() => resolve(data),
|
||||
this.requestResetTime - Date.now() + this.timeDifference + this.restManager.client.options.restTimeOffset
|
||||
);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handle() {
|
||||
super.handle();
|
||||
if (this.busy || this.remaining === 0 || this.queue.length === 0 || this.globalLimit) return;
|
||||
this.execute(this.queue.shift()).then(() => {
|
||||
this.busy = false;
|
||||
this.handle();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SequentialRequestHandler;
|
||||
25
node_modules/discord.js/src/client/rest/UserAgentManager.js
generated
vendored
Normal file
25
node_modules/discord.js/src/client/rest/UserAgentManager.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class UserAgentManager {
|
||||
constructor() {
|
||||
this.build(this.constructor.DEFAULT);
|
||||
}
|
||||
|
||||
set({ url, version } = {}) {
|
||||
this.build({
|
||||
url: url || this.constructor.DFEAULT.url,
|
||||
version: version || this.constructor.DEFAULT.version,
|
||||
});
|
||||
}
|
||||
|
||||
build(ua) {
|
||||
this.userAgent = `DiscordBot (${ua.url}, ${ua.version}) Node.js/${process.version}`;
|
||||
}
|
||||
}
|
||||
|
||||
UserAgentManager.DEFAULT = {
|
||||
url: Constants.Package.homepage.split('#')[0],
|
||||
version: Constants.Package.version,
|
||||
};
|
||||
|
||||
module.exports = UserAgentManager;
|
||||
81
node_modules/discord.js/src/client/voice/ClientVoiceManager.js
generated
vendored
Normal file
81
node_modules/discord.js/src/client/voice/ClientVoiceManager.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
const Collection = require('../../util/Collection');
|
||||
const VoiceConnection = require('./VoiceConnection');
|
||||
|
||||
/**
|
||||
* Manages all the voice stuff for the client.
|
||||
* @private
|
||||
*/
|
||||
class ClientVoiceManager {
|
||||
constructor(client) {
|
||||
/**
|
||||
* The client that instantiated this voice manager
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = client;
|
||||
|
||||
/**
|
||||
* A collection mapping connection IDs to the Connection objects
|
||||
* @type {Collection<Snowflake, VoiceConnection>}
|
||||
*/
|
||||
this.connections = new Collection();
|
||||
|
||||
this.client.on('self.voiceServer', this.onVoiceServer.bind(this));
|
||||
this.client.on('self.voiceStateUpdate', this.onVoiceStateUpdate.bind(this));
|
||||
}
|
||||
|
||||
onVoiceServer({ guild_id, token, endpoint }) {
|
||||
const connection = this.connections.get(guild_id);
|
||||
if (connection) connection.setTokenAndEndpoint(token, endpoint);
|
||||
}
|
||||
|
||||
onVoiceStateUpdate({ guild_id, session_id, channel_id }) {
|
||||
const connection = this.connections.get(guild_id);
|
||||
if (connection) {
|
||||
connection.channel = this.client.channels.get(channel_id);
|
||||
connection.setSessionID(session_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a request to join a voice channel.
|
||||
* @param {VoiceChannel} channel The voice channel to join
|
||||
* @returns {Promise<VoiceConnection>}
|
||||
*/
|
||||
joinChannel(channel) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!channel.joinable) {
|
||||
if (channel.full) {
|
||||
throw new Error('You do not have permission to join this voice channel; it is full.');
|
||||
} else {
|
||||
throw new Error('You do not have permission to join this voice channel.');
|
||||
}
|
||||
}
|
||||
|
||||
let connection = this.connections.get(channel.guild.id);
|
||||
|
||||
if (connection) {
|
||||
if (connection.channel.id !== channel.id) {
|
||||
this.connections.get(channel.guild.id).updateChannel(channel);
|
||||
}
|
||||
resolve(connection);
|
||||
return;
|
||||
} else {
|
||||
connection = new VoiceConnection(this, channel);
|
||||
this.connections.set(channel.guild.id, connection);
|
||||
}
|
||||
|
||||
connection.once('failed', reason => {
|
||||
this.connections.delete(channel.guild.id);
|
||||
reject(reason);
|
||||
});
|
||||
|
||||
connection.once('authenticated', () => {
|
||||
connection.once('ready', () => resolve(connection));
|
||||
connection.once('error', reject);
|
||||
connection.once('disconnect', () => this.connections.delete(channel.guild.id));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientVoiceManager;
|
||||
366
node_modules/discord.js/src/client/voice/VoiceBroadcast.js
generated
vendored
Normal file
366
node_modules/discord.js/src/client/voice/VoiceBroadcast.js
generated
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
const VolumeInterface = require('./util/VolumeInterface');
|
||||
const Prism = require('prism-media');
|
||||
const OpusEncoders = require('./opus/OpusEngineList');
|
||||
const Collection = require('../../util/Collection');
|
||||
|
||||
const ffmpegArguments = [
|
||||
'-analyzeduration', '0',
|
||||
'-loglevel', '0',
|
||||
'-f', 's16le',
|
||||
'-ar', '48000',
|
||||
'-ac', '2',
|
||||
];
|
||||
|
||||
/**
|
||||
* A voice broadcast can be played across multiple voice connections for improved shared-stream efficiency.
|
||||
*
|
||||
* Example usage:
|
||||
* ```js
|
||||
* const broadcast = client.createVoiceBroadcast();
|
||||
* broadcast.playFile('./music.mp3');
|
||||
* // Play "music.mp3" in all voice connections that the client is in
|
||||
* for (const connection of client.voiceConnections.values()) {
|
||||
* connection.playBroadcast(broadcast);
|
||||
* }
|
||||
* ```
|
||||
* @implements {VolumeInterface}
|
||||
*/
|
||||
class VoiceBroadcast extends VolumeInterface {
|
||||
constructor(client) {
|
||||
super();
|
||||
/**
|
||||
* The client that created the broadcast
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = client;
|
||||
this._dispatchers = new Collection();
|
||||
this._encoders = new Collection();
|
||||
/**
|
||||
* The audio transcoder that this broadcast uses
|
||||
* @type {Prism}
|
||||
*/
|
||||
this.prism = new Prism();
|
||||
/**
|
||||
* The current audio transcoder that is being used
|
||||
* @type {Object}
|
||||
*/
|
||||
this.currentTranscoder = null;
|
||||
this.tickInterval = null;
|
||||
this._volume = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of subscribed dispatchers
|
||||
* @type {StreamDispatcher[]}
|
||||
* @readonly
|
||||
*/
|
||||
get dispatchers() {
|
||||
let d = [];
|
||||
for (const container of this._dispatchers.values()) {
|
||||
d = d.concat(Array.from(container.values()));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
get _playableStream() {
|
||||
const currentTranscoder = this.currentTranscoder;
|
||||
if (!currentTranscoder) return null;
|
||||
const transcoder = currentTranscoder.transcoder;
|
||||
const options = currentTranscoder.options;
|
||||
return (transcoder && transcoder.output) || options.stream;
|
||||
}
|
||||
|
||||
unregisterDispatcher(dispatcher, old) {
|
||||
const volume = old || dispatcher.volume;
|
||||
|
||||
/**
|
||||
* Emitted whenever a stream dispatcher unsubscribes from the broadcast.
|
||||
* @event VoiceBroadcast#unsubscribe
|
||||
* @param {StreamDispatcher} dispatcher The unsubscribed dispatcher
|
||||
*/
|
||||
this.emit('unsubscribe', dispatcher);
|
||||
for (const container of this._dispatchers.values()) {
|
||||
container.delete(dispatcher);
|
||||
|
||||
if (!container.size) {
|
||||
this._encoders.get(volume).destroy();
|
||||
this._dispatchers.delete(volume);
|
||||
this._encoders.delete(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerDispatcher(dispatcher) {
|
||||
if (!this._dispatchers.has(dispatcher.volume)) {
|
||||
this._dispatchers.set(dispatcher.volume, new Set());
|
||||
this._encoders.set(dispatcher.volume, OpusEncoders.fetch());
|
||||
}
|
||||
const container = this._dispatchers.get(dispatcher.volume);
|
||||
if (!container.has(dispatcher)) {
|
||||
container.add(dispatcher);
|
||||
dispatcher.once('end', () => this.unregisterDispatcher(dispatcher));
|
||||
dispatcher.on('volumeChange', (o, n) => {
|
||||
this.unregisterDispatcher(dispatcher, o);
|
||||
if (!this._dispatchers.has(n)) {
|
||||
this._dispatchers.set(n, new Set());
|
||||
this._encoders.set(n, OpusEncoders.fetch());
|
||||
}
|
||||
this._dispatchers.get(n).add(dispatcher);
|
||||
});
|
||||
/**
|
||||
* Emitted whenever a stream dispatcher subscribes to the broadcast.
|
||||
* @event VoiceBroadcast#subscribe
|
||||
* @param {StreamDispatcher} dispatcher The subscribed dispatcher
|
||||
*/
|
||||
this.emit('subscribe', dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
killCurrentTranscoder() {
|
||||
if (this.currentTranscoder) {
|
||||
if (this.currentTranscoder.transcoder) this.currentTranscoder.transcoder.kill();
|
||||
this.currentTranscoder = null;
|
||||
this.emit('end');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays any audio stream across the broadcast.
|
||||
* @param {ReadableStream} stream The audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {VoiceBroadcast}
|
||||
* @example
|
||||
* // Play streams using ytdl-core
|
||||
* const ytdl = require('ytdl-core');
|
||||
* const streamOptions = { seek: 0, volume: 1 };
|
||||
* const broadcast = client.createVoiceBroadcast();
|
||||
*
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
* const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
|
||||
* broadcast.playStream(stream);
|
||||
* const dispatcher = connection.playBroadcast(broadcast);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
playStream(stream, options = {}) {
|
||||
this.setVolume(options.volume || 1);
|
||||
return this._playTranscodable(stream, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the given file in the voice connection.
|
||||
* @param {string} file The absolute path to the file
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
* @example
|
||||
* // Play files natively
|
||||
* const broadcast = client.createVoiceBroadcast();
|
||||
*
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
* broadcast.playFile('C:/Users/Discord/Desktop/music.mp3');
|
||||
* const dispatcher = connection.playBroadcast(broadcast);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
playFile(file, options = {}) {
|
||||
this.setVolume(options.volume || 1);
|
||||
return this._playTranscodable(`file:${file}`, options);
|
||||
}
|
||||
|
||||
_playTranscodable(media, options) {
|
||||
this.killCurrentTranscoder();
|
||||
const transcoder = this.prism.transcode({
|
||||
type: 'ffmpeg',
|
||||
media,
|
||||
ffmpegArguments: ffmpegArguments.concat(['-ss', String(options.seek || 0)]),
|
||||
});
|
||||
/**
|
||||
* Emitted whenever an error occurs.
|
||||
* @event VoiceBroadcast#error
|
||||
* @param {Error} error The error that occurred
|
||||
*/
|
||||
transcoder.once('error', e => {
|
||||
if (this.listenerCount('error') > 0) this.emit('error', e);
|
||||
/**
|
||||
* Emitted whenever the VoiceBroadcast has any warnings.
|
||||
* @event VoiceBroadcast#warn
|
||||
* @param {string|Error} warning The warning that was raised
|
||||
*/
|
||||
else this.emit('warn', e);
|
||||
});
|
||||
/**
|
||||
* Emitted once the broadcast (the audio stream) ends.
|
||||
* @event VoiceBroadcast#end
|
||||
*/
|
||||
transcoder.once('end', () => this.killCurrentTranscoder());
|
||||
this.currentTranscoder = {
|
||||
transcoder,
|
||||
options,
|
||||
};
|
||||
transcoder.output.once('readable', () => this._startPlaying());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a stream of 16-bit signed stereo PCM.
|
||||
* @param {ReadableStream} stream The audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {VoiceBroadcast}
|
||||
*/
|
||||
playConvertedStream(stream, options = {}) {
|
||||
this.killCurrentTranscoder();
|
||||
this.setVolume(options.volume || 1);
|
||||
this.currentTranscoder = { options: { stream } };
|
||||
stream.once('readable', () => this._startPlaying());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an Opus encoded stream.
|
||||
* <warn>Note that inline volume is not compatible with this method.</warn>
|
||||
* @param {ReadableStream} stream The Opus audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
*/
|
||||
playOpusStream(stream) {
|
||||
this.currentTranscoder = { options: { stream }, opus: true };
|
||||
stream.once('readable', () => this._startPlaying());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
|
||||
* @param {string} input The arbitrary input
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {VoiceBroadcast}
|
||||
*/
|
||||
playArbitraryInput(input, options = {}) {
|
||||
this.setVolume(options.volume || 1);
|
||||
options.input = input;
|
||||
return this._playTranscodable(input, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the entire broadcast - all dispatchers also pause.
|
||||
*/
|
||||
pause() {
|
||||
this.paused = true;
|
||||
for (const container of this._dispatchers.values()) {
|
||||
for (const dispatcher of container.values()) {
|
||||
dispatcher.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the entire broadcast - all dispatchers also resume.
|
||||
*/
|
||||
resume() {
|
||||
this.paused = false;
|
||||
for (const container of this._dispatchers.values()) {
|
||||
for (const dispatcher of container.values()) {
|
||||
dispatcher.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_startPlaying() {
|
||||
if (this.tickInterval) clearInterval(this.tickInterval);
|
||||
// Old code?
|
||||
// this.tickInterval = this.client.setInterval(this.tick.bind(this), 20);
|
||||
this._startTime = Date.now();
|
||||
this._count = 0;
|
||||
this._pausedTime = 0;
|
||||
this._missed = 0;
|
||||
this.tick();
|
||||
}
|
||||
|
||||
tick() {
|
||||
if (!this._playableStream) return;
|
||||
if (this.paused) {
|
||||
this._pausedTime += 20;
|
||||
setTimeout(() => this.tick(), 20);
|
||||
return;
|
||||
}
|
||||
|
||||
const opus = this.currentTranscoder.opus;
|
||||
const buffer = this.readStreamBuffer();
|
||||
|
||||
if (!buffer) {
|
||||
this._missed++;
|
||||
if (this._missed < 5) {
|
||||
this._pausedTime += 200;
|
||||
setTimeout(() => this.tick(), 200);
|
||||
} else {
|
||||
this.killCurrentTranscoder();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._missed = 0;
|
||||
|
||||
let packetMatrix = {};
|
||||
|
||||
const getOpusPacket = volume => {
|
||||
if (packetMatrix[volume]) return packetMatrix[volume];
|
||||
|
||||
const opusEncoder = this._encoders.get(volume);
|
||||
const opusPacket = opusEncoder.encode(this.applyVolume(buffer, this._volume * volume));
|
||||
packetMatrix[volume] = opusPacket;
|
||||
return opusPacket;
|
||||
};
|
||||
|
||||
for (const dispatcher of this.dispatchers) {
|
||||
if (opus) {
|
||||
dispatcher.processPacket(buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
const volume = dispatcher.volume;
|
||||
dispatcher.processPacket(getOpusPacket(volume));
|
||||
}
|
||||
|
||||
const next = 20 + (this._startTime + this._pausedTime + (this._count * 20) - Date.now());
|
||||
this._count++;
|
||||
setTimeout(() => this.tick(), next);
|
||||
}
|
||||
|
||||
readStreamBuffer() {
|
||||
const opus = this.currentTranscoder.opus;
|
||||
const bufferLength = (opus ? 80 : 1920) * 2;
|
||||
let buffer = this._playableStream.read(bufferLength);
|
||||
if (opus) return buffer;
|
||||
if (!buffer) return null;
|
||||
|
||||
if (buffer.length !== bufferLength) {
|
||||
const newBuffer = Buffer.alloc(bufferLength).fill(0);
|
||||
buffer.copy(newBuffer);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the current stream from playing without unsubscribing dispatchers.
|
||||
*/
|
||||
end() {
|
||||
this.killCurrentTranscoder();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the current broadcast, all subscribed dispatchers will also end.
|
||||
*/
|
||||
destroy() {
|
||||
this.end();
|
||||
for (const container of this._dispatchers.values()) {
|
||||
for (const dispatcher of container.values()) {
|
||||
dispatcher.destroy('end', 'broadcast ended');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceBroadcast;
|
||||
535
node_modules/discord.js/src/client/voice/VoiceConnection.js
generated
vendored
Normal file
535
node_modules/discord.js/src/client/voice/VoiceConnection.js
generated
vendored
Normal file
@@ -0,0 +1,535 @@
|
||||
const VoiceWebSocket = require('./VoiceWebSocket');
|
||||
const VoiceUDP = require('./VoiceUDPClient');
|
||||
const Util = require('../../util/Util');
|
||||
const Constants = require('../../util/Constants');
|
||||
const AudioPlayer = require('./player/AudioPlayer');
|
||||
const VoiceReceiver = require('./receiver/VoiceReceiver');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Prism = require('prism-media');
|
||||
|
||||
/**
|
||||
* Represents a connection to a guild's voice server.
|
||||
* ```js
|
||||
* // Obtained using:
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
*
|
||||
* });
|
||||
* ```
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class VoiceConnection extends EventEmitter {
|
||||
constructor(voiceManager, channel) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* The voice manager that instantiated this connection
|
||||
* @type {ClientVoiceManager}
|
||||
*/
|
||||
this.voiceManager = voiceManager;
|
||||
|
||||
/**
|
||||
* The client that instantiated this connection
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = voiceManager.client;
|
||||
|
||||
/**
|
||||
* @external Prism
|
||||
* @see {@link https://github.com/hydrabolt/prism-media}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The audio transcoder for this connection
|
||||
* @type {Prism}
|
||||
*/
|
||||
this.prism = new Prism();
|
||||
|
||||
/**
|
||||
* The voice channel this connection is currently serving
|
||||
* @type {VoiceChannel}
|
||||
*/
|
||||
this.channel = channel;
|
||||
|
||||
/**
|
||||
* The current status of the voice connection
|
||||
* @type {number}
|
||||
*/
|
||||
this.status = Constants.VoiceStatus.AUTHENTICATING;
|
||||
|
||||
/**
|
||||
* Whether we're currently transmitting audio
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.speaking = false;
|
||||
|
||||
/**
|
||||
* An array of Voice Receivers that have been created for this connection
|
||||
* @type {VoiceReceiver[]}
|
||||
*/
|
||||
this.receivers = [];
|
||||
|
||||
/**
|
||||
* The authentication data needed to connect to the voice server
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.authentication = {};
|
||||
|
||||
/**
|
||||
* The audio player for this voice connection
|
||||
* @type {AudioPlayer}
|
||||
*/
|
||||
this.player = new AudioPlayer(this);
|
||||
|
||||
this.player.on('debug', m => {
|
||||
/**
|
||||
* Debug info from the connection.
|
||||
* @event VoiceConnection#debug
|
||||
* @param {string} message The debug message
|
||||
*/
|
||||
this.emit('debug', `audio player - ${m}`);
|
||||
});
|
||||
|
||||
this.player.on('error', e => {
|
||||
/**
|
||||
* Warning info from the connection.
|
||||
* @event VoiceConnection#warn
|
||||
* @param {string|Error} warning The warning
|
||||
*/
|
||||
this.emit('warn', e);
|
||||
});
|
||||
|
||||
/**
|
||||
* Map SSRC to speaking values
|
||||
* @type {Map<number, boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.ssrcMap = new Map();
|
||||
|
||||
/**
|
||||
* Object that wraps contains the `ws` and `udp` sockets of this voice connection
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.sockets = {};
|
||||
|
||||
this.authenticate();
|
||||
}
|
||||
|
||||
/**
|
||||
* The current stream dispatcher (if any)
|
||||
* @type {?StreamDispatcher}
|
||||
* @readonly
|
||||
*/
|
||||
get dispatcher() {
|
||||
return this.player.dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the voice connection should display as "speaking" or not.
|
||||
* @param {boolean} value Whether or not to speak
|
||||
* @private
|
||||
*/
|
||||
setSpeaking(value) {
|
||||
if (this.speaking === value) return;
|
||||
if (this.status !== Constants.VoiceStatus.CONNECTED) return;
|
||||
this.speaking = value;
|
||||
this.sockets.ws.sendPacket({
|
||||
op: Constants.VoiceOPCodes.SPEAKING,
|
||||
d: {
|
||||
speaking: true,
|
||||
delay: 0,
|
||||
},
|
||||
}).catch(e => {
|
||||
this.emit('debug', e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to the main gateway to join a voice channel.
|
||||
* @param {Object} [options] The options to provide
|
||||
*/
|
||||
sendVoiceStateUpdate(options = {}) {
|
||||
options = Util.mergeDefault({
|
||||
guild_id: this.channel.guild.id,
|
||||
channel_id: this.channel.id,
|
||||
self_mute: false,
|
||||
self_deaf: false,
|
||||
}, options);
|
||||
|
||||
this.client.ws.send({
|
||||
op: Constants.OPCodes.VOICE_STATE_UPDATE,
|
||||
d: options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the token and endpoint required to connect to the voice servers.
|
||||
* @param {string} token The voice token
|
||||
* @param {string} endpoint The voice endpoint
|
||||
* @returns {void}
|
||||
*/
|
||||
setTokenAndEndpoint(token, endpoint) {
|
||||
if (!endpoint) {
|
||||
// Signifies awaiting endpoint stage
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
this.authenticateFailed('Token not provided from voice server packet.');
|
||||
return;
|
||||
}
|
||||
|
||||
endpoint = endpoint.match(/([^:]*)/)[0];
|
||||
|
||||
if (!endpoint) {
|
||||
this.authenticateFailed('Invalid endpoint received.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
|
||||
this.authentication.token = token;
|
||||
this.authentication.endpoint = endpoint;
|
||||
this.checkAuthenticated();
|
||||
} else if (token !== this.authentication.token || endpoint !== this.authentication.endpoint) {
|
||||
this.reconnect(token, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Session ID for the connection.
|
||||
* @param {string} sessionID The voice session ID
|
||||
*/
|
||||
setSessionID(sessionID) {
|
||||
if (!sessionID) {
|
||||
this.authenticateFailed('Session ID not supplied.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
|
||||
this.authentication.sessionID = sessionID;
|
||||
this.checkAuthenticated();
|
||||
} else if (sessionID !== this.authentication.sessionID) {
|
||||
this.authentication.sessionID = sessionID;
|
||||
/**
|
||||
* Emitted when a new session ID is received.
|
||||
* @event VoiceConnection#newSession
|
||||
* @private
|
||||
*/
|
||||
this.emit('newSession', sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the voice connection is authenticated.
|
||||
* @private
|
||||
*/
|
||||
checkAuthenticated() {
|
||||
const { token, endpoint, sessionID } = this.authentication;
|
||||
|
||||
if (token && endpoint && sessionID) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.status = Constants.VoiceStatus.CONNECTING;
|
||||
/**
|
||||
* Emitted when we successfully initiate a voice connection.
|
||||
* @event VoiceConnection#authenticated
|
||||
*/
|
||||
this.emit('authenticated');
|
||||
this.connect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when we fail to initiate a voice connection.
|
||||
* @param {string} reason The reason for failure
|
||||
* @private
|
||||
*/
|
||||
authenticateFailed(reason) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
if (this.status === Constants.VoiceStatus.AUTHENTICATING) {
|
||||
/**
|
||||
* Emitted when we fail to initiate a voice connection.
|
||||
* @event VoiceConnection#failed
|
||||
* @param {Error} error The encountered error
|
||||
*/
|
||||
this.emit('failed', new Error(reason));
|
||||
} else {
|
||||
this.emit('error', new Error(reason));
|
||||
}
|
||||
this.status = Constants.VoiceStatus.DISCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to a different voice channel in the same guild.
|
||||
* @param {VoiceChannel} channel The channel to move to
|
||||
* @private
|
||||
*/
|
||||
updateChannel(channel) {
|
||||
this.channel = channel;
|
||||
this.sendVoiceStateUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to authenticate to the voice server.
|
||||
* @private
|
||||
*/
|
||||
authenticate() {
|
||||
this.sendVoiceStateUpdate();
|
||||
this.connectTimeout = this.client.setTimeout(
|
||||
() => this.authenticateFailed(new Error('Connection not established within 15 seconds.')), 15000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to reconnect to the voice server (typically after a region change).
|
||||
* @param {string} token The voice token
|
||||
* @param {string} endpoint The voice endpoint
|
||||
* @private
|
||||
*/
|
||||
reconnect(token, endpoint) {
|
||||
this.authentication.token = token;
|
||||
this.authentication.endpoint = endpoint;
|
||||
|
||||
this.status = Constants.VoiceStatus.RECONNECTING;
|
||||
/**
|
||||
* Emitted when the voice connection is reconnecting (typically after a region change).
|
||||
* @event VoiceConnection#reconnecting
|
||||
*/
|
||||
this.emit('reconnecting');
|
||||
this.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect the voice connection, causing a disconnect and closing event to be emitted.
|
||||
*/
|
||||
disconnect() {
|
||||
this.emit('closing');
|
||||
this.sendVoiceStateUpdate({
|
||||
channel_id: null,
|
||||
});
|
||||
this.player.destroy();
|
||||
this.cleanup();
|
||||
this.status = Constants.VoiceStatus.DISCONNECTED;
|
||||
/**
|
||||
* Emitted when the voice connection disconnects.
|
||||
* @event VoiceConnection#disconnect
|
||||
*/
|
||||
this.emit('disconnect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up after disconnect.
|
||||
* @private
|
||||
*/
|
||||
cleanup() {
|
||||
const { ws, udp } = this.sockets;
|
||||
|
||||
if (ws) {
|
||||
ws.removeAllListeners('error');
|
||||
ws.removeAllListeners('ready');
|
||||
ws.removeAllListeners('sessionDescription');
|
||||
ws.removeAllListeners('speaking');
|
||||
}
|
||||
|
||||
if (udp) udp.removeAllListeners('error');
|
||||
|
||||
this.sockets.ws = null;
|
||||
this.sockets.udp = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the voice connection.
|
||||
* @private
|
||||
*/
|
||||
connect() {
|
||||
if (this.status !== Constants.VoiceStatus.RECONNECTING) {
|
||||
if (this.sockets.ws) throw new Error('There is already an existing WebSocket connection.');
|
||||
if (this.sockets.udp) throw new Error('There is already an existing UDP connection.');
|
||||
}
|
||||
|
||||
if (this.sockets.ws) this.sockets.ws.shutdown();
|
||||
if (this.sockets.udp) this.sockets.udp.shutdown();
|
||||
|
||||
this.sockets.ws = new VoiceWebSocket(this);
|
||||
this.sockets.udp = new VoiceUDP(this);
|
||||
|
||||
const { ws, udp } = this.sockets;
|
||||
|
||||
ws.on('error', err => this.emit('error', err));
|
||||
udp.on('error', err => this.emit('error', err));
|
||||
ws.on('ready', this.onReady.bind(this));
|
||||
ws.on('sessionDescription', this.onSessionDescription.bind(this));
|
||||
ws.on('speaking', this.onSpeaking.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the voice websocket is ready.
|
||||
* @param {Object} data The received data
|
||||
* @private
|
||||
*/
|
||||
onReady({ port, ssrc }) {
|
||||
this.authentication.port = port;
|
||||
this.authentication.ssrc = ssrc;
|
||||
|
||||
const udp = this.sockets.udp;
|
||||
/**
|
||||
* Emitted whenever the connection encounters an error.
|
||||
* @event VoiceConnection#error
|
||||
* @param {Error} error The encountered error
|
||||
*/
|
||||
udp.findEndpointAddress()
|
||||
.then(address => {
|
||||
udp.createUDPSocket(address);
|
||||
}, e => this.emit('error', e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a session description is received.
|
||||
* @param {string} mode The encryption mode
|
||||
* @param {string} secret The secret key
|
||||
* @private
|
||||
*/
|
||||
onSessionDescription(mode, secret) {
|
||||
this.authentication.encryptionMode = mode;
|
||||
this.authentication.secretKey = secret;
|
||||
|
||||
this.status = Constants.VoiceStatus.CONNECTED;
|
||||
/**
|
||||
* Emitted once the connection is ready, when a promise to join a voice channel resolves,
|
||||
* the connection will already be ready.
|
||||
* @event VoiceConnection#ready
|
||||
*/
|
||||
this.emit('ready');
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a speaking event is received.
|
||||
* @param {Object} data The received data
|
||||
* @private
|
||||
*/
|
||||
onSpeaking({ user_id, ssrc, speaking }) {
|
||||
const guild = this.channel.guild;
|
||||
const user = this.client.users.get(user_id);
|
||||
this.ssrcMap.set(+ssrc, user);
|
||||
if (!speaking) {
|
||||
for (const receiver of this.receivers) {
|
||||
receiver.stoppedSpeaking(user);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
if (this.status === Constants.VoiceStatus.CONNECTED) this.emit('speaking', user, speaking);
|
||||
guild._memberSpeakUpdate(user_id, speaking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options that can be passed to stream-playing methods:
|
||||
* @typedef {Object} StreamOptions
|
||||
* @property {number} [seek=0] The time to seek to
|
||||
* @property {number} [volume=1] The volume to play at
|
||||
* @property {number} [passes=1] How many times to send the voice packet to reduce packet loss
|
||||
* @property {number|string} [bitrate=48000] The bitrate (quality) of the audio.
|
||||
* If set to 'auto', the voice channel's bitrate will be used
|
||||
*/
|
||||
|
||||
/**
|
||||
* Play the given file in the voice connection.
|
||||
* @param {string} file The absolute path to the file
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
* @example
|
||||
* // Play files natively
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
* const dispatcher = connection.playFile('C:/Users/Discord/Desktop/music.mp3');
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
playFile(file, options) {
|
||||
return this.player.playUnknownStream(`file:${file}`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an arbitrary input that can be [handled by ffmpeg](https://ffmpeg.org/ffmpeg-protocols.html#Description)
|
||||
* @param {string} input the arbitrary input
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
*/
|
||||
playArbitraryInput(input, options) {
|
||||
return this.player.playUnknownStream(input, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays and converts an audio stream in the voice connection.
|
||||
* @param {ReadableStream} stream The audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
* @example
|
||||
* // Play streams using ytdl-core
|
||||
* const ytdl = require('ytdl-core');
|
||||
* const streamOptions = { seek: 0, volume: 1 };
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
* const stream = ytdl('https://www.youtube.com/watch?v=XAWgeLF9EVQ', { filter : 'audioonly' });
|
||||
* const dispatcher = connection.playStream(stream, streamOptions);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
playStream(stream, options) {
|
||||
return this.player.playUnknownStream(stream, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a stream of 16-bit signed stereo PCM.
|
||||
* @param {ReadableStream} stream The audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
*/
|
||||
playConvertedStream(stream, options) {
|
||||
return this.player.playPCMStream(stream, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays an Opus encoded stream.
|
||||
* <warn>Note that inline volume is not compatible with this method.</warn>
|
||||
* @param {ReadableStream} stream The Opus audio stream to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
*/
|
||||
playOpusStream(stream, options) {
|
||||
return this.player.playOpusStream(stream, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a voice broadcast.
|
||||
* @param {VoiceBroadcast} broadcast The broadcast to play
|
||||
* @param {StreamOptions} [options] Options for playing the stream
|
||||
* @returns {StreamDispatcher}
|
||||
* @example
|
||||
* // Play a broadcast
|
||||
* const broadcast = client
|
||||
* .createVoiceBroadcast()
|
||||
* .playFile('./test.mp3');
|
||||
* const dispatcher = voiceConnection.playBroadcast(broadcast);
|
||||
*/
|
||||
playBroadcast(broadcast, options) {
|
||||
return this.player.playBroadcast(broadcast, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a VoiceReceiver so you can start listening to voice data.
|
||||
* It's recommended to only create one of these.
|
||||
* @returns {VoiceReceiver}
|
||||
*/
|
||||
createReceiver() {
|
||||
const receiver = new VoiceReceiver(this);
|
||||
this.receivers.push(receiver);
|
||||
return receiver;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceConnection;
|
||||
145
node_modules/discord.js/src/client/voice/VoiceUDPClient.js
generated
vendored
Normal file
145
node_modules/discord.js/src/client/voice/VoiceUDPClient.js
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
const udp = require('dgram');
|
||||
const dns = require('dns');
|
||||
const Constants = require('../../util/Constants');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* Represents a UDP client for a Voice Connection.
|
||||
* @extends {EventEmitter}
|
||||
* @private
|
||||
*/
|
||||
class VoiceConnectionUDPClient extends EventEmitter {
|
||||
constructor(voiceConnection) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* The voice connection that this UDP client serves
|
||||
* @type {VoiceConnection}
|
||||
*/
|
||||
this.voiceConnection = voiceConnection;
|
||||
|
||||
/**
|
||||
* The UDP socket
|
||||
* @type {?Socket}
|
||||
*/
|
||||
this.socket = null;
|
||||
|
||||
/**
|
||||
* The address of the Discord voice server
|
||||
* @type {?string}
|
||||
*/
|
||||
this.discordAddress = null;
|
||||
|
||||
/**
|
||||
* The local IP address
|
||||
* @type {?string}
|
||||
*/
|
||||
this.localAddress = null;
|
||||
|
||||
/**
|
||||
* The local port
|
||||
* @type {?string}
|
||||
*/
|
||||
this.localPort = null;
|
||||
|
||||
this.voiceConnection.on('closing', this.shutdown.bind(this));
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
if (this.socket) {
|
||||
this.socket.removeAllListeners('message');
|
||||
try {
|
||||
this.socket.close();
|
||||
} finally {
|
||||
this.socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The port of the Discord voice server
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get discordPort() {
|
||||
return this.voiceConnection.authentication.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resolve the voice server endpoint to an address.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
findEndpointAddress() {
|
||||
return new Promise((resolve, reject) => {
|
||||
dns.lookup(this.voiceConnection.authentication.endpoint, (error, address) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
this.discordAddress = address;
|
||||
resolve(address);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to the UDP client.
|
||||
* @param {Object} packet The packet to send
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
send(packet) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.socket) throw new Error('Tried to send a UDP packet, but there is no socket available.');
|
||||
if (!this.discordAddress || !this.discordPort) throw new Error('Malformed UDP address or port.');
|
||||
this.socket.send(packet, 0, packet.length, this.discordPort, this.discordAddress, error => {
|
||||
if (error) reject(error); else resolve(packet);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createUDPSocket(address) {
|
||||
this.discordAddress = address;
|
||||
const socket = this.socket = udp.createSocket('udp4');
|
||||
|
||||
socket.once('message', message => {
|
||||
const packet = parseLocalPacket(message);
|
||||
if (packet.error) {
|
||||
this.emit('error', packet.error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.localAddress = packet.address;
|
||||
this.localPort = packet.port;
|
||||
|
||||
this.voiceConnection.sockets.ws.sendPacket({
|
||||
op: Constants.VoiceOPCodes.SELECT_PROTOCOL,
|
||||
d: {
|
||||
protocol: 'udp',
|
||||
data: {
|
||||
address: packet.address,
|
||||
port: packet.port,
|
||||
mode: 'xsalsa20_poly1305',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const blankMessage = Buffer.alloc(70);
|
||||
blankMessage.writeUIntBE(this.voiceConnection.authentication.ssrc, 0, 4);
|
||||
this.send(blankMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function parseLocalPacket(message) {
|
||||
try {
|
||||
const packet = Buffer.from(message);
|
||||
let address = '';
|
||||
for (let i = 4; i < packet.indexOf(0, i); i++) address += String.fromCharCode(packet[i]);
|
||||
const port = parseInt(packet.readUIntLE(packet.length - 2, 2).toString(10), 10);
|
||||
return { address, port };
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceConnectionUDPClient;
|
||||
246
node_modules/discord.js/src/client/voice/VoiceWebSocket.js
generated
vendored
Normal file
246
node_modules/discord.js/src/client/voice/VoiceWebSocket.js
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
const Constants = require('../../util/Constants');
|
||||
const SecretKey = require('./util/SecretKey');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
|
||||
let WebSocket;
|
||||
try {
|
||||
WebSocket = require('uws');
|
||||
} catch (err) {
|
||||
WebSocket = require('ws');
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Voice Connection's WebSocket.
|
||||
* @extends {EventEmitter}
|
||||
* @private
|
||||
*/
|
||||
class VoiceWebSocket extends EventEmitter {
|
||||
constructor(voiceConnection) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* The client of this voice WebSocket
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = voiceConnection.voiceManager.client;
|
||||
|
||||
/**
|
||||
* The Voice Connection that this WebSocket serves
|
||||
* @type {VoiceConnection}
|
||||
*/
|
||||
this.voiceConnection = voiceConnection;
|
||||
|
||||
/**
|
||||
* How many connection attempts have been made
|
||||
* @type {number}
|
||||
*/
|
||||
this.attempts = 0;
|
||||
|
||||
this.connect();
|
||||
this.dead = false;
|
||||
this.voiceConnection.on('closing', this.shutdown.bind(this));
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
this.dead = true;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current WebSocket.
|
||||
*/
|
||||
reset() {
|
||||
if (this.ws) {
|
||||
if (this.ws.readyState !== WebSocket.CLOSED) this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
this.clearHeartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts connecting to the Voice WebSocket Server.
|
||||
*/
|
||||
connect() {
|
||||
if (this.dead) return;
|
||||
if (this.ws) this.reset();
|
||||
if (this.attempts >= 5) {
|
||||
this.emit('debug', new Error(`Too many connection attempts (${this.attempts}).`));
|
||||
return;
|
||||
}
|
||||
|
||||
this.attempts++;
|
||||
|
||||
/**
|
||||
* The actual WebSocket used to connect to the Voice WebSocket Server.
|
||||
* @type {WebSocket}
|
||||
*/
|
||||
this.ws = new WebSocket(`wss://${this.voiceConnection.authentication.endpoint}`);
|
||||
this.ws.onopen = this.onOpen.bind(this);
|
||||
this.ws.onmessage = this.onMessage.bind(this);
|
||||
this.ws.onclose = this.onClose.bind(this);
|
||||
this.ws.onerror = this.onError.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends data to the WebSocket if it is open.
|
||||
* @param {string} data The data to send to the WebSocket
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
send(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
||||
throw new Error(`Voice websocket not open to send ${data}.`);
|
||||
}
|
||||
this.ws.send(data, null, error => {
|
||||
if (error) reject(error); else resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON.stringify's a packet and then sends it to the WebSocket Server.
|
||||
* @param {Object} packet The packet to send
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
sendPacket(packet) {
|
||||
try {
|
||||
packet = JSON.stringify(packet);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return this.send(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the WebSocket opens.
|
||||
*/
|
||||
onOpen() {
|
||||
this.sendPacket({
|
||||
op: Constants.OPCodes.DISPATCH,
|
||||
d: {
|
||||
server_id: this.voiceConnection.channel.guild.id,
|
||||
user_id: this.client.user.id,
|
||||
token: this.voiceConnection.authentication.token,
|
||||
session_id: this.voiceConnection.authentication.sessionID,
|
||||
},
|
||||
}).catch(() => {
|
||||
this.emit('error', new Error('Tried to send join packet, but the WebSocket is not open.'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a message is received from the WebSocket.
|
||||
* @param {MessageEvent} event The message event that was received
|
||||
* @returns {void}
|
||||
*/
|
||||
onMessage(event) {
|
||||
try {
|
||||
return this.onPacket(JSON.parse(event.data));
|
||||
} catch (error) {
|
||||
return this.onError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the connection to the WebSocket server is lost.
|
||||
*/
|
||||
onClose() {
|
||||
if (!this.dead) this.client.setTimeout(this.connect.bind(this), this.attempts * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever an error occurs with the WebSocket.
|
||||
* @param {Error} error The error that occurred
|
||||
*/
|
||||
onError(error) {
|
||||
this.emit('error', error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a valid packet is received from the WebSocket.
|
||||
* @param {Object} packet The received packet
|
||||
*/
|
||||
onPacket(packet) {
|
||||
switch (packet.op) {
|
||||
case Constants.VoiceOPCodes.READY:
|
||||
this.setHeartbeat(packet.d.heartbeat_interval);
|
||||
/**
|
||||
* Emitted once the voice WebSocket receives the ready packet.
|
||||
* @param {Object} packet The received packet
|
||||
* @event VoiceWebSocket#ready
|
||||
*/
|
||||
this.emit('ready', packet.d);
|
||||
break;
|
||||
case Constants.VoiceOPCodes.SESSION_DESCRIPTION:
|
||||
/**
|
||||
* Emitted once the Voice Websocket receives a description of this voice session.
|
||||
* @param {string} encryptionMode The type of encryption being used
|
||||
* @param {SecretKey} secretKey The secret key used for encryption
|
||||
* @event VoiceWebSocket#sessionDescription
|
||||
*/
|
||||
this.emit('sessionDescription', packet.d.mode, new SecretKey(packet.d.secret_key));
|
||||
break;
|
||||
case Constants.VoiceOPCodes.SPEAKING:
|
||||
/**
|
||||
* Emitted whenever a speaking packet is received.
|
||||
* @param {Object} data
|
||||
* @event VoiceWebSocket#speaking
|
||||
*/
|
||||
this.emit('speaking', packet.d);
|
||||
break;
|
||||
default:
|
||||
/**
|
||||
* Emitted when an unhandled packet is received.
|
||||
* @param {Object} packet
|
||||
* @event VoiceWebSocket#unknownPacket
|
||||
*/
|
||||
this.emit('unknownPacket', packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval at which to send a heartbeat packet to the WebSocket.
|
||||
* @param {number} interval The interval at which to send a heartbeat packet
|
||||
*/
|
||||
setHeartbeat(interval) {
|
||||
if (!interval || isNaN(interval)) {
|
||||
this.onError(new Error('Tried to set voice heartbeat but no valid interval was specified.'));
|
||||
return;
|
||||
}
|
||||
if (this.heartbeatInterval) {
|
||||
/**
|
||||
* Emitted whenver the voice WebSocket encounters a non-fatal error.
|
||||
* @param {string} warn The warning
|
||||
* @event VoiceWebSocket#warn
|
||||
*/
|
||||
this.emit('warn', 'A voice heartbeat interval is being overwritten');
|
||||
clearInterval(this.heartbeatInterval);
|
||||
}
|
||||
this.heartbeatInterval = this.client.setInterval(this.sendHeartbeat.bind(this), interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a heartbeat interval, if one exists.
|
||||
*/
|
||||
clearHeartbeat() {
|
||||
if (!this.heartbeatInterval) {
|
||||
this.emit('warn', 'Tried to clear a heartbeat interval that does not exist');
|
||||
return;
|
||||
}
|
||||
clearInterval(this.heartbeatInterval);
|
||||
this.heartbeatInterval = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a heartbeat packet.
|
||||
*/
|
||||
sendHeartbeat() {
|
||||
this.sendPacket({ op: Constants.VoiceOPCodes.HEARTBEAT, d: null }).catch(() => {
|
||||
this.emit('warn', 'Tried to send heartbeat, but connection is not open');
|
||||
this.clearHeartbeat();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceWebSocket;
|
||||
331
node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
generated
vendored
Normal file
331
node_modules/discord.js/src/client/voice/dispatcher/StreamDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
const VolumeInterface = require('../util/VolumeInterface');
|
||||
const VoiceBroadcast = require('../VoiceBroadcast');
|
||||
const Constants = require('../../../util/Constants');
|
||||
|
||||
const secretbox = require('../util/Secretbox');
|
||||
|
||||
const nonce = Buffer.alloc(24);
|
||||
nonce.fill(0);
|
||||
|
||||
/**
|
||||
* The class that sends voice packet data to the voice connection.
|
||||
* ```js
|
||||
* // Obtained using:
|
||||
* voiceChannel.join().then(connection => {
|
||||
* // You can play a file or a stream here:
|
||||
* const dispatcher = connection.playFile('./file.mp3');
|
||||
* });
|
||||
* ```
|
||||
* @implements {VolumeInterface}
|
||||
*/
|
||||
class StreamDispatcher extends VolumeInterface {
|
||||
constructor(player, stream, streamOptions) {
|
||||
super(streamOptions);
|
||||
/**
|
||||
* The Audio Player that controls this dispatcher
|
||||
* @type {AudioPlayer}
|
||||
*/
|
||||
this.player = player;
|
||||
/**
|
||||
* The stream that the dispatcher plays
|
||||
* @type {ReadableStream|VoiceBroadcast}
|
||||
*/
|
||||
this.stream = stream;
|
||||
if (!(this.stream instanceof VoiceBroadcast)) this.startStreaming();
|
||||
this.streamOptions = streamOptions;
|
||||
|
||||
const data = this.streamingData;
|
||||
data.length = 20;
|
||||
data.missed = 0;
|
||||
|
||||
/**
|
||||
* Whether playing is paused
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.paused = false;
|
||||
/**
|
||||
* Whether this dispatcher has been destroyed
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.destroyed = false;
|
||||
|
||||
this._opus = streamOptions.opus;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many passes the dispatcher should take when sending packets to reduce packet loss. Values over 5
|
||||
* aren't recommended, as it means you are using 5x more bandwidth. You _can_ edit this at runtime
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get passes() {
|
||||
return this.streamOptions.passes || 1;
|
||||
}
|
||||
|
||||
set passes(n) {
|
||||
this.streamOptions.passes = n;
|
||||
}
|
||||
|
||||
get streamingData() {
|
||||
return this.player.streamingData;
|
||||
}
|
||||
|
||||
/**
|
||||
* How long the stream dispatcher has been "speaking" for
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get time() {
|
||||
return this.streamingData.count * (this.streamingData.length || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The total time, taking into account pauses and skips, that the dispatcher has been streaming for
|
||||
* @type {number}
|
||||
* @readonly
|
||||
*/
|
||||
get totalStreamTime() {
|
||||
return this.time + this.streamingData.pausedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops sending voice packets to the voice connection (stream may still progress however).
|
||||
*/
|
||||
pause() { this.setPaused(true); }
|
||||
|
||||
/**
|
||||
* Resumes sending voice packets to the voice connection (may be further on in the stream than when paused).
|
||||
*/
|
||||
resume() { this.setPaused(false); }
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current stream permanently and emits an `end` event.
|
||||
* @param {string} [reason='user'] An optional reason for stopping the dispatcher
|
||||
*/
|
||||
end(reason = 'user') {
|
||||
this.destroy('end', reason);
|
||||
}
|
||||
|
||||
setSpeaking(value) {
|
||||
if (this.speaking === value) return;
|
||||
if (this.player.voiceConnection.status !== Constants.VoiceStatus.CONNECTED) return;
|
||||
this.speaking = value;
|
||||
/**
|
||||
* Emitted when the dispatcher starts/stops speaking.
|
||||
* @event StreamDispatcher#speaking
|
||||
* @param {boolean} value Whether or not the dispatcher is speaking
|
||||
*/
|
||||
this.emit('speaking', value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the bitrate of the current Opus encoder.
|
||||
* @param {number} bitrate New bitrate, in kbps
|
||||
* If set to 'auto', the voice channel's bitrate will be used
|
||||
*/
|
||||
setBitrate(bitrate) {
|
||||
this.player.setBitrate(bitrate);
|
||||
}
|
||||
|
||||
sendBuffer(buffer, sequence, timestamp, opusPacket) {
|
||||
opusPacket = opusPacket || this.player.opusEncoder.encode(buffer);
|
||||
const packet = this.createPacket(sequence, timestamp, opusPacket);
|
||||
this.sendPacket(packet);
|
||||
}
|
||||
|
||||
sendPacket(packet) {
|
||||
let repeats = this.passes;
|
||||
/**
|
||||
* Emitted whenever the dispatcher has debug information.
|
||||
* @event StreamDispatcher#debug
|
||||
* @param {string} info The debug info
|
||||
*/
|
||||
this.setSpeaking(true);
|
||||
while (repeats--) {
|
||||
this.player.voiceConnection.sockets.udp.send(packet)
|
||||
.catch(e => {
|
||||
this.setSpeaking(false);
|
||||
this.emit('debug', `Failed to send a packet ${e}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createPacket(sequence, timestamp, buffer) {
|
||||
const packetBuffer = Buffer.alloc(buffer.length + 28);
|
||||
packetBuffer.fill(0);
|
||||
packetBuffer[0] = 0x80;
|
||||
packetBuffer[1] = 0x78;
|
||||
|
||||
packetBuffer.writeUIntBE(sequence, 2, 2);
|
||||
packetBuffer.writeUIntBE(timestamp, 4, 4);
|
||||
packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc, 8, 4);
|
||||
|
||||
packetBuffer.copy(nonce, 0, 0, 12);
|
||||
buffer = secretbox.methods.close(buffer, nonce, this.player.voiceConnection.authentication.secretKey.key);
|
||||
for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i];
|
||||
|
||||
return packetBuffer;
|
||||
}
|
||||
|
||||
processPacket(packet) {
|
||||
try {
|
||||
if (this.destroyed) {
|
||||
this.setSpeaking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = this.streamingData;
|
||||
|
||||
if (this.paused) {
|
||||
this.setSpeaking(false);
|
||||
data.pausedTime = data.length * 10;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!packet) {
|
||||
data.missed++;
|
||||
data.pausedTime += data.length * 10;
|
||||
return;
|
||||
}
|
||||
|
||||
this.started();
|
||||
this.missed = 0;
|
||||
|
||||
this.stepStreamingData();
|
||||
this.sendBuffer(null, data.sequence, data.timestamp, packet);
|
||||
} catch (e) {
|
||||
this.destroy('error', e);
|
||||
}
|
||||
}
|
||||
|
||||
process() {
|
||||
try {
|
||||
if (this.destroyed) {
|
||||
this.setSpeaking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = this.streamingData;
|
||||
|
||||
if (data.missed >= 5) {
|
||||
this.destroy('end', 'Stream is not generating quickly enough.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.paused) {
|
||||
this.setSpeaking(false);
|
||||
// Old code?
|
||||
// data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
|
||||
data.pausedTime += data.length * 10;
|
||||
this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
|
||||
return;
|
||||
}
|
||||
|
||||
this.started();
|
||||
|
||||
const buffer = this.readStreamBuffer();
|
||||
if (!buffer) {
|
||||
data.missed++;
|
||||
data.pausedTime += data.length * 10;
|
||||
this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
|
||||
return;
|
||||
}
|
||||
|
||||
data.missed = 0;
|
||||
|
||||
this.stepStreamingData();
|
||||
|
||||
if (this._opus) {
|
||||
this.sendBuffer(null, data.sequence, data.timestamp, buffer);
|
||||
} else {
|
||||
this.sendBuffer(buffer, data.sequence, data.timestamp);
|
||||
}
|
||||
|
||||
const nextTime = data.length + (data.startTime + data.pausedTime + (data.count * data.length) - Date.now());
|
||||
this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), nextTime);
|
||||
} catch (e) {
|
||||
this.destroy('error', e);
|
||||
}
|
||||
}
|
||||
|
||||
readStreamBuffer() {
|
||||
const data = this.streamingData;
|
||||
const bufferLength = (this._opus ? 80 : 1920) * data.channels;
|
||||
let buffer = this.stream.read(bufferLength);
|
||||
if (this._opus) return buffer;
|
||||
if (!buffer) return null;
|
||||
|
||||
if (buffer.length !== bufferLength) {
|
||||
const newBuffer = Buffer.alloc(bufferLength).fill(0);
|
||||
buffer.copy(newBuffer);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
|
||||
buffer = this.applyVolume(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
started() {
|
||||
const data = this.streamingData;
|
||||
|
||||
if (!data.startTime) {
|
||||
/**
|
||||
* Emitted once the dispatcher starts streaming.
|
||||
* @event StreamDispatcher#start
|
||||
*/
|
||||
this.emit('start');
|
||||
data.startTime = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
stepStreamingData() {
|
||||
const data = this.streamingData;
|
||||
data.count++;
|
||||
data.sequence = data.sequence < 65535 ? data.sequence + 1 : 0;
|
||||
data.timestamp = (data.timestamp + 960) < 4294967295 ? data.timestamp + 960 : 0;
|
||||
}
|
||||
|
||||
destroy(type, reason) {
|
||||
if (this.destroyed) return;
|
||||
this.destroyed = true;
|
||||
this.setSpeaking(false);
|
||||
this.emit(type, reason);
|
||||
/**
|
||||
* Emitted once the dispatcher ends.
|
||||
* @param {string} [reason] The reason the dispatcher ended
|
||||
* @event StreamDispatcher#end
|
||||
*/
|
||||
if (type !== 'end') this.emit('end', `destroyed due to ${type} - ${reason}`);
|
||||
}
|
||||
|
||||
startStreaming() {
|
||||
if (!this.stream) {
|
||||
/**
|
||||
* Emitted if the dispatcher encounters an error.
|
||||
* @event StreamDispatcher#error
|
||||
* @param {string} error The error message
|
||||
*/
|
||||
this.emit('error', 'No stream');
|
||||
return;
|
||||
}
|
||||
|
||||
this.stream.on('end', err => this.destroy('end', err || 'stream'));
|
||||
this.stream.on('error', err => this.destroy('error', err));
|
||||
|
||||
const data = this.streamingData;
|
||||
data.length = 20;
|
||||
data.missed = 0;
|
||||
|
||||
this.stream.once('readable', () => {
|
||||
data.startTime = null;
|
||||
data.count = 0;
|
||||
this.process();
|
||||
});
|
||||
}
|
||||
|
||||
setPaused(paused) { this.setSpeaking(!(this.paused = paused)); }
|
||||
}
|
||||
|
||||
module.exports = StreamDispatcher;
|
||||
60
node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
generated
vendored
Normal file
60
node_modules/discord.js/src/client/voice/opus/BaseOpusEngine.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* The base opus encoding engine.
|
||||
* @private
|
||||
*/
|
||||
class BaseOpus {
|
||||
/**
|
||||
* @param {Object} [options] The options to apply to the Opus engine
|
||||
* @param {number} [options.bitrate=48] The desired bitrate (kbps)
|
||||
* @param {boolean} [options.fec=false] Whether to enable forward error correction
|
||||
* @param {number} [options.plp=0] The expected packet loss percentage
|
||||
*/
|
||||
constructor({ bitrate = 48, fec = false, plp = 0 } = {}) {
|
||||
this.ctl = {
|
||||
BITRATE: 4002,
|
||||
FEC: 4012,
|
||||
PLP: 4014,
|
||||
};
|
||||
|
||||
this.samplingRate = 48000;
|
||||
this.channels = 2;
|
||||
|
||||
/**
|
||||
* The desired bitrate (kbps)
|
||||
* @type {number}
|
||||
*/
|
||||
this.bitrate = bitrate;
|
||||
|
||||
/**
|
||||
* Miscellaneous Opus options
|
||||
* @type {Object}
|
||||
*/
|
||||
this.options = { fec, plp };
|
||||
}
|
||||
|
||||
init() {
|
||||
try {
|
||||
this.setBitrate(this.bitrate);
|
||||
|
||||
// Set FEC (forward error correction)
|
||||
if (this.options.fec) this.setFEC(this.options.fec);
|
||||
|
||||
// Set PLP (expected packet loss percentage)
|
||||
if (this.options.plp) this.setPLP(this.options.plp);
|
||||
} catch (err) {
|
||||
// Opus engine likely has no support for libopus CTL
|
||||
}
|
||||
}
|
||||
|
||||
encode(buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
decode(buffer) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
destroy() {} // eslint-disable-line no-empty-function
|
||||
}
|
||||
|
||||
module.exports = BaseOpus;
|
||||
40
node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
generated
vendored
Normal file
40
node_modules/discord.js/src/client/voice/opus/NodeOpusEngine.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
const OpusEngine = require('./BaseOpusEngine');
|
||||
|
||||
let opus;
|
||||
|
||||
class NodeOpusEngine extends OpusEngine {
|
||||
constructor(player) {
|
||||
super(player);
|
||||
try {
|
||||
opus = require('node-opus');
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
this.encoder = new opus.OpusEncoder(this.samplingRate, this.channels);
|
||||
super.init();
|
||||
}
|
||||
|
||||
setBitrate(bitrate) {
|
||||
this.encoder.applyEncoderCTL(this.ctl.BITRATE, Math.min(128, Math.max(16, bitrate)) * 1000);
|
||||
}
|
||||
|
||||
setFEC(enabled) {
|
||||
this.encoder.applyEncoderCTL(this.ctl.FEC, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
setPLP(percent) {
|
||||
this.encoder.applyEncoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
|
||||
}
|
||||
|
||||
encode(buffer) {
|
||||
super.encode(buffer);
|
||||
return this.encoder.encode(buffer, 1920);
|
||||
}
|
||||
|
||||
decode(buffer) {
|
||||
super.decode(buffer);
|
||||
return this.encoder.decode(buffer, 1920);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NodeOpusEngine;
|
||||
28
node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
generated
vendored
Normal file
28
node_modules/discord.js/src/client/voice/opus/OpusEngineList.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
const list = [
|
||||
require('./NodeOpusEngine'),
|
||||
require('./OpusScriptEngine'),
|
||||
];
|
||||
|
||||
function fetch(Encoder, engineOptions) {
|
||||
try {
|
||||
return new Encoder(engineOptions);
|
||||
} catch (err) {
|
||||
if (err.message.includes('Cannot find module')) return null;
|
||||
|
||||
// The Opus engine exists, but another error occurred.
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
exports.add = encoder => {
|
||||
list.push(encoder);
|
||||
};
|
||||
|
||||
exports.fetch = engineOptions => {
|
||||
for (const encoder of list) {
|
||||
const fetched = fetch(encoder, engineOptions);
|
||||
if (fetched) return fetched;
|
||||
}
|
||||
|
||||
throw new Error('OPUS_ENGINE_MISSING');
|
||||
};
|
||||
45
node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
generated
vendored
Normal file
45
node_modules/discord.js/src/client/voice/opus/OpusScriptEngine.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
const OpusEngine = require('./BaseOpusEngine');
|
||||
|
||||
let OpusScript;
|
||||
|
||||
class OpusScriptEngine extends OpusEngine {
|
||||
constructor(player) {
|
||||
super(player);
|
||||
try {
|
||||
OpusScript = require('opusscript');
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
this.encoder = new OpusScript(this.samplingRate, this.channels);
|
||||
super.init();
|
||||
}
|
||||
|
||||
setBitrate(bitrate) {
|
||||
this.encoder.encoderCTL(this.ctl.BITRATE, Math.min(128, Math.max(16, bitrate)) * 1000);
|
||||
}
|
||||
|
||||
setFEC(enabled) {
|
||||
this.encoder.encoderCTL(this.ctl.FEC, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
setPLP(percent) {
|
||||
this.encoder.encoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
|
||||
}
|
||||
|
||||
encode(buffer) {
|
||||
super.encode(buffer);
|
||||
return this.encoder.encode(buffer, 960);
|
||||
}
|
||||
|
||||
decode(buffer) {
|
||||
super.decode(buffer);
|
||||
return this.encoder.decode(buffer);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.encoder.delete();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OpusScriptEngine;
|
||||
170
node_modules/discord.js/src/client/voice/player/AudioPlayer.js
generated
vendored
Normal file
170
node_modules/discord.js/src/client/voice/player/AudioPlayer.js
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Prism = require('prism-media');
|
||||
const StreamDispatcher = require('../dispatcher/StreamDispatcher');
|
||||
const Collection = require('../../../util/Collection');
|
||||
const OpusEncoders = require('../opus/OpusEngineList');
|
||||
|
||||
const ffmpegArguments = [
|
||||
'-analyzeduration', '0',
|
||||
'-loglevel', '0',
|
||||
'-f', 's16le',
|
||||
'-ar', '48000',
|
||||
'-ac', '2',
|
||||
];
|
||||
|
||||
/**
|
||||
* An Audio Player for a Voice Connection.
|
||||
* @private
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class AudioPlayer extends EventEmitter {
|
||||
constructor(voiceConnection) {
|
||||
super();
|
||||
/**
|
||||
* The voice connection that the player serves
|
||||
* @type {VoiceConnection}
|
||||
*/
|
||||
this.voiceConnection = voiceConnection;
|
||||
/**
|
||||
* The prism transcoder that the player uses
|
||||
* @type {Prism}
|
||||
*/
|
||||
this.prism = new Prism();
|
||||
this.streams = new Collection();
|
||||
this.currentStream = {};
|
||||
this.streamingData = {
|
||||
channels: 2,
|
||||
count: 0,
|
||||
sequence: 0,
|
||||
timestamp: 0,
|
||||
pausedTime: 0,
|
||||
};
|
||||
this.voiceConnection.once('closing', () => this.destroyCurrentStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* The current transcoder
|
||||
* @type {?Object}
|
||||
* @readonly
|
||||
*/
|
||||
get transcoder() {
|
||||
return this.currentStream.transcoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current dispatcher
|
||||
* @type {?StreamDispatcher}
|
||||
* @readonly
|
||||
*/
|
||||
get dispatcher() {
|
||||
return this.currentStream.dispatcher;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.opusEncoder) this.opusEncoder.destroy();
|
||||
this.opusEncoder = null;
|
||||
}
|
||||
|
||||
destroyCurrentStream() {
|
||||
const transcoder = this.transcoder;
|
||||
const dispatcher = this.dispatcher;
|
||||
if (transcoder) transcoder.kill();
|
||||
if (dispatcher) {
|
||||
const end = dispatcher.listeners('end')[0];
|
||||
const error = dispatcher.listeners('error')[0];
|
||||
if (end) dispatcher.removeListener('end', end);
|
||||
if (error) dispatcher.removeListener('error', error);
|
||||
dispatcher.destroy('end');
|
||||
}
|
||||
this.currentStream = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bitrate of the current Opus encoder.
|
||||
* @param {number} value New bitrate, in kbps
|
||||
* If set to 'auto', the voice channel's bitrate will be used
|
||||
*/
|
||||
setBitrate(value) {
|
||||
if (!value) return;
|
||||
if (!this.opusEncoder) return;
|
||||
const bitrate = value === 'auto' ? this.voiceConnection.channel.bitrate : value;
|
||||
this.opusEncoder.setBitrate(bitrate);
|
||||
}
|
||||
|
||||
playUnknownStream(stream, options = {}) {
|
||||
this.destroy();
|
||||
this.opusEncoder = OpusEncoders.fetch(options);
|
||||
const transcoder = this.prism.transcode({
|
||||
type: 'ffmpeg',
|
||||
media: stream,
|
||||
ffmpegArguments: ffmpegArguments.concat(['-ss', String(options.seek || 0)]),
|
||||
});
|
||||
this.destroyCurrentStream();
|
||||
this.currentStream = {
|
||||
transcoder: transcoder,
|
||||
output: transcoder.output,
|
||||
input: stream,
|
||||
};
|
||||
transcoder.on('error', e => {
|
||||
this.destroyCurrentStream();
|
||||
if (this.listenerCount('error') > 0) this.emit('error', e);
|
||||
this.emit('warn', `prism transcoder error - ${e}`);
|
||||
});
|
||||
return this.playPCMStream(transcoder.output, options, true);
|
||||
}
|
||||
|
||||
playPCMStream(stream, options = {}, fromUnknown = false) {
|
||||
this.destroy();
|
||||
this.opusEncoder = OpusEncoders.fetch(options);
|
||||
this.setBitrate(options.bitrate);
|
||||
const dispatcher = this.createDispatcher(stream, options);
|
||||
if (fromUnknown) {
|
||||
this.currentStream.dispatcher = dispatcher;
|
||||
} else {
|
||||
this.destroyCurrentStream();
|
||||
this.currentStream = {
|
||||
dispatcher,
|
||||
input: stream,
|
||||
output: stream,
|
||||
};
|
||||
}
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
playOpusStream(stream, options = {}) {
|
||||
options.opus = true;
|
||||
this.destroyCurrentStream();
|
||||
const dispatcher = this.createDispatcher(stream, options);
|
||||
this.currentStream = {
|
||||
dispatcher,
|
||||
input: stream,
|
||||
output: stream,
|
||||
};
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
playBroadcast(broadcast, options) {
|
||||
this.destroyCurrentStream();
|
||||
const dispatcher = this.createDispatcher(broadcast, options);
|
||||
this.currentStream = {
|
||||
dispatcher,
|
||||
broadcast,
|
||||
input: broadcast,
|
||||
output: broadcast,
|
||||
};
|
||||
broadcast.registerDispatcher(dispatcher);
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
createDispatcher(stream, { seek = 0, volume = 1, passes = 1, opus } = {}) {
|
||||
const options = { seek, volume, passes, opus };
|
||||
|
||||
const dispatcher = new StreamDispatcher(this, stream, options);
|
||||
dispatcher.on('end', () => this.destroyCurrentStream());
|
||||
dispatcher.on('error', () => this.destroyCurrentStream());
|
||||
dispatcher.on('speaking', value => this.voiceConnection.setSpeaking(value));
|
||||
return dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AudioPlayer;
|
||||
17
node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/voice/receiver/VoiceReadable.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const Readable = require('stream').Readable;
|
||||
|
||||
class VoiceReadable extends Readable {
|
||||
constructor() {
|
||||
super();
|
||||
this._packets = [];
|
||||
this.open = true;
|
||||
}
|
||||
|
||||
_read() {} // eslint-disable-line no-empty-function
|
||||
|
||||
_push(d) {
|
||||
if (this.open) this.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceReadable;
|
||||
219
node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
generated
vendored
Normal file
219
node_modules/discord.js/src/client/voice/receiver/VoiceReceiver.js
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const secretbox = require('../util/Secretbox');
|
||||
const Readable = require('./VoiceReadable');
|
||||
const OpusEncoders = require('../opus/OpusEngineList');
|
||||
|
||||
const nonce = Buffer.alloc(24);
|
||||
nonce.fill(0);
|
||||
|
||||
/**
|
||||
* Receives voice data from a voice connection.
|
||||
* ```js
|
||||
* // Obtained using:
|
||||
* voiceChannel.join()
|
||||
* .then(connection => {
|
||||
* const receiver = connection.createReceiver();
|
||||
* });
|
||||
* ```
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class VoiceReceiver extends EventEmitter {
|
||||
constructor(connection) {
|
||||
super();
|
||||
/*
|
||||
Need a queue because we don't get the ssrc of the user speaking until after the first few packets,
|
||||
so we queue up unknown SSRCs until they become known, then empty the queue
|
||||
*/
|
||||
this.queues = new Map();
|
||||
this.pcmStreams = new Map();
|
||||
this.opusStreams = new Map();
|
||||
this.opusEncoders = new Map();
|
||||
|
||||
/**
|
||||
* Whether or not this receiver has been destroyed
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.destroyed = false;
|
||||
|
||||
/**
|
||||
* The VoiceConnection that instantiated this
|
||||
* @type {VoiceConnection}
|
||||
*/
|
||||
this.voiceConnection = connection;
|
||||
|
||||
this._listener = msg => {
|
||||
const ssrc = +msg.readUInt32BE(8).toString(10);
|
||||
const user = this.voiceConnection.ssrcMap.get(ssrc);
|
||||
if (!user) {
|
||||
if (!this.queues.has(ssrc)) this.queues.set(ssrc, []);
|
||||
this.queues.get(ssrc).push(msg);
|
||||
} else {
|
||||
if (this.queues.get(ssrc)) {
|
||||
this.queues.get(ssrc).push(msg);
|
||||
this.queues.get(ssrc).map(m => this.handlePacket(m, user));
|
||||
this.queues.delete(ssrc);
|
||||
return;
|
||||
}
|
||||
this.handlePacket(msg, user);
|
||||
}
|
||||
};
|
||||
this.voiceConnection.sockets.udp.socket.on('message', this._listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this VoiceReceiver has been destroyed, running `recreate()` will recreate the listener.
|
||||
* This avoids you having to create a new receiver.
|
||||
* <info>Any streams that you had prior to destroying the receiver will not be recreated.</info>
|
||||
*/
|
||||
recreate() {
|
||||
if (!this.destroyed) return;
|
||||
this.voiceConnection.sockets.udp.socket.on('message', this._listener);
|
||||
this.destroyed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy this VoiceReceiver, also ending any streams that it may be controlling.
|
||||
*/
|
||||
destroy() {
|
||||
this.voiceConnection.sockets.udp.socket.removeListener('message', this._listener);
|
||||
for (const [id, stream] of this.pcmStreams) {
|
||||
stream._push(null);
|
||||
this.pcmStreams.delete(id);
|
||||
}
|
||||
for (const [id, stream] of this.opusStreams) {
|
||||
stream._push(null);
|
||||
this.opusStreams.delete(id);
|
||||
}
|
||||
for (const [id, encoder] of this.opusEncoders) {
|
||||
encoder.destroy();
|
||||
this.opusEncoders.delete(id);
|
||||
}
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a user stops speaking.
|
||||
* @param {User} user The user that stopped speaking
|
||||
* @private
|
||||
*/
|
||||
stoppedSpeaking(user) {
|
||||
const opusStream = this.opusStreams.get(user.id);
|
||||
const pcmStream = this.pcmStreams.get(user.id);
|
||||
const opusEncoder = this.opusEncoders.get(user.id);
|
||||
if (opusStream) {
|
||||
opusStream.push(null);
|
||||
opusStream.open = false;
|
||||
this.opusStreams.delete(user.id);
|
||||
}
|
||||
if (pcmStream) {
|
||||
pcmStream.push(null);
|
||||
pcmStream.open = false;
|
||||
this.pcmStreams.delete(user.id);
|
||||
}
|
||||
if (opusEncoder) {
|
||||
opusEncoder.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @returns {ReadableStream}
|
||||
*/
|
||||
createOpusStream(user) {
|
||||
user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
|
||||
if (!user) throw new Error('Couldn\'t resolve the user to create Opus stream.');
|
||||
if (this.opusStreams.get(user.id)) throw new Error('There is already an existing stream for that user.');
|
||||
const stream = new Readable();
|
||||
this.opusStreams.set(user.id, stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 32-bit signed stereo PCM at 48KHz.
|
||||
* @param {UserResolvable} user The user to create the stream for
|
||||
* @returns {ReadableStream}
|
||||
*/
|
||||
createPCMStream(user) {
|
||||
user = this.voiceConnection.voiceManager.client.resolver.resolveUser(user);
|
||||
if (!user) throw new Error('Couldn\'t resolve the user to create PCM stream.');
|
||||
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;
|
||||
}
|
||||
|
||||
handlePacket(msg, user) {
|
||||
msg.copy(nonce, 0, 0, 12);
|
||||
let data = secretbox.methods.open(msg.slice(12), nonce, this.voiceConnection.authentication.secretKey.key);
|
||||
if (!data) {
|
||||
/**
|
||||
* Emitted whenever a voice packet experiences a problem.
|
||||
* @event VoiceReceiver#warn
|
||||
* @param {string} reason The reason for the warning. If it happened because the voice packet could not be
|
||||
* decrypted, this would be `decrypt`. If it happened because the voice packet could not be decoded into
|
||||
* PCM, this would be `decode`
|
||||
* @param {string} message The warning message
|
||||
*/
|
||||
this.emit('warn', 'decrypt', 'Failed to decrypt voice packet');
|
||||
return;
|
||||
}
|
||||
data = Buffer.from(data);
|
||||
|
||||
// Strip RTP Header Extensions (one-byte only)
|
||||
if (data[0] === 0xBE && data[1] === 0xDE && data.length > 4) {
|
||||
const headerExtensionLength = data.readUInt16BE(2);
|
||||
let offset = 4;
|
||||
for (let i = 0; i < headerExtensionLength; i++) {
|
||||
const byte = data[offset];
|
||||
offset++;
|
||||
if (byte === 0) {
|
||||
continue;
|
||||
}
|
||||
offset += 1 + (0b1111 & (byte >> 4));
|
||||
}
|
||||
while (data[offset] === 0) {
|
||||
offset++;
|
||||
}
|
||||
data = data.slice(offset);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!this.opusEncoders.get(user.id)) this.opusEncoders.set(user.id, OpusEncoders.fetch());
|
||||
const { pcm, error } = VoiceReceiver._tryDecode(this.opusEncoders.get(user.id), data);
|
||||
if (error) {
|
||||
this.emit('warn', 'decode', `Failed to decode packet voice to PCM because: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
if (this.pcmStreams.get(user.id)) this.pcmStreams.get(user.id)._push(pcm);
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
this.emit('pcm', user, pcm);
|
||||
}
|
||||
}
|
||||
|
||||
static _tryDecode(encoder, data) {
|
||||
try {
|
||||
return { pcm: encoder.decode(data) };
|
||||
} catch (error) {
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceReceiver;
|
||||
16
node_modules/discord.js/src/client/voice/util/SecretKey.js
generated
vendored
Normal file
16
node_modules/discord.js/src/client/voice/util/SecretKey.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Represents a Secret Key used in encryption over voice.
|
||||
* @private
|
||||
*/
|
||||
class SecretKey {
|
||||
constructor(key) {
|
||||
/**
|
||||
* The key used for encryption
|
||||
* @type {Uint8Array}
|
||||
*/
|
||||
this.key = new Uint8Array(new ArrayBuffer(key.length));
|
||||
for (const index in key) this.key[index] = key[index];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SecretKey;
|
||||
33
node_modules/discord.js/src/client/voice/util/Secretbox.js
generated
vendored
Normal file
33
node_modules/discord.js/src/client/voice/util/Secretbox.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
const libs = {
|
||||
sodium: sodium => ({
|
||||
open: sodium.api.crypto_secretbox_open_easy,
|
||||
close: sodium.api.crypto_secretbox_easy,
|
||||
}),
|
||||
'libsodium-wrappers': sodium => ({
|
||||
open: sodium.crypto_secretbox_open_easy,
|
||||
close: sodium.crypto_secretbox_easy,
|
||||
}),
|
||||
tweetnacl: tweetnacl => ({
|
||||
open: tweetnacl.secretbox.open,
|
||||
close: tweetnacl.secretbox,
|
||||
}),
|
||||
};
|
||||
|
||||
exports.methods = {};
|
||||
|
||||
for (const libName of Object.keys(libs)) {
|
||||
try {
|
||||
const lib = require(libName);
|
||||
if (libName === 'libsodium-wrappers' && lib.ready) {
|
||||
lib.ready.then(() => {
|
||||
exports.methods = libs[libName](lib);
|
||||
}).catch(() => {
|
||||
const tweetnacl = require('tweetnacl');
|
||||
exports.methods = libs.tweetnacl(tweetnacl);
|
||||
}).catch(() => undefined);
|
||||
} else {
|
||||
exports.methods = libs[libName](lib);
|
||||
}
|
||||
break;
|
||||
} catch (err) {} // eslint-disable-line no-empty
|
||||
}
|
||||
86
node_modules/discord.js/src/client/voice/util/VolumeInterface.js
generated
vendored
Normal file
86
node_modules/discord.js/src/client/voice/util/VolumeInterface.js
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
const EventEmitter = require('events');
|
||||
|
||||
/**
|
||||
* An interface class for volume transformation.
|
||||
* @extends {EventEmitter}
|
||||
*/
|
||||
class VolumeInterface extends EventEmitter {
|
||||
constructor({ volume = 0 } = {}) {
|
||||
super();
|
||||
this.setVolume(volume || 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current volume of the broadcast
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
get volume() {
|
||||
return this._volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current volume of the broadcast in decibels
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
get volumeDecibels() {
|
||||
return Math.log10(this._volume) * 20;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current volume of the broadcast from a logarithmic scale
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
get volumeLogarithmic() {
|
||||
return Math.pow(this._volume, 1 / 1.660964);
|
||||
}
|
||||
|
||||
applyVolume(buffer, volume) {
|
||||
volume = volume || this._volume;
|
||||
if (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;
|
||||
const uint = Math.min(32767, Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))));
|
||||
out.writeInt16LE(uint, i);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
setVolume(volume) {
|
||||
/**
|
||||
* Emitted when the volume of this interface changes.
|
||||
* @event VolumeInterface#volumeChange
|
||||
* @param {number} oldVolume The old volume of this interface
|
||||
* @param {number} newVolume The new volume of this interface
|
||||
*/
|
||||
this.emit('volumeChange', this._volume, volume);
|
||||
this._volume = volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume in decibels.
|
||||
* @param {number} db The decibels
|
||||
*/
|
||||
setVolumeDecibels(db) {
|
||||
this.setVolume(Math.pow(10, db / 20));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
setVolumeLogarithmic(value) {
|
||||
this.setVolume(Math.pow(value, 1.660964));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VolumeInterface;
|
||||
506
node_modules/discord.js/src/client/websocket/WebSocketConnection.js
generated
vendored
Normal file
506
node_modules/discord.js/src/client/websocket/WebSocketConnection.js
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
const browser = typeof window !== 'undefined';
|
||||
const EventEmitter = require('events');
|
||||
const Constants = require('../../util/Constants');
|
||||
const zlib = require('zlib');
|
||||
const PacketManager = require('./packets/WebSocketPacketManager');
|
||||
const erlpack = (function findErlpack() {
|
||||
try {
|
||||
const e = require('erlpack');
|
||||
if (!e.pack) return null;
|
||||
return e;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}());
|
||||
|
||||
const WebSocket = (function findWebSocket() {
|
||||
if (browser) return window.WebSocket; // eslint-disable-line no-undef
|
||||
try {
|
||||
return require('uws');
|
||||
} catch (e) {
|
||||
return require('ws');
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* Abstracts a WebSocket connection with decoding/encoding for the Discord gateway.
|
||||
* @private
|
||||
*/
|
||||
class WebSocketConnection extends EventEmitter {
|
||||
/**
|
||||
* @param {WebSocketManager} manager The WebSocket manager
|
||||
* @param {string} gateway The WebSocket gateway to connect to
|
||||
*/
|
||||
constructor(manager, gateway) {
|
||||
super();
|
||||
/**
|
||||
* The WebSocket Manager of this connection
|
||||
* @type {WebSocketManager}
|
||||
*/
|
||||
this.manager = manager;
|
||||
|
||||
/**
|
||||
* The client this belongs to
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = manager.client;
|
||||
|
||||
/**
|
||||
* The WebSocket connection itself
|
||||
* @type {WebSocket}
|
||||
*/
|
||||
this.ws = null;
|
||||
|
||||
/**
|
||||
* The current sequence of the WebSocket
|
||||
* @type {number}
|
||||
*/
|
||||
this.sequence = -1;
|
||||
|
||||
/**
|
||||
* The current status of the client
|
||||
* @type {number}
|
||||
*/
|
||||
this.status = Constants.Status.IDLE;
|
||||
|
||||
/**
|
||||
* The Packet Manager of the connection
|
||||
* @type {WebSocketPacketManager}
|
||||
*/
|
||||
this.packetManager = new PacketManager(this);
|
||||
|
||||
/**
|
||||
* The last time a ping was sent (a timestamp)
|
||||
* @type {number}
|
||||
*/
|
||||
this.lastPingTimestamp = 0;
|
||||
|
||||
/**
|
||||
* Contains the rate limit queue and metadata
|
||||
* @type {Object}
|
||||
*/
|
||||
this.ratelimit = {
|
||||
queue: [],
|
||||
remaining: 120,
|
||||
total: 120,
|
||||
time: 60e3,
|
||||
resetTimer: null,
|
||||
};
|
||||
this.connect(gateway);
|
||||
|
||||
/**
|
||||
* Events that are disabled (will not be processed)
|
||||
* @type {Object}
|
||||
*/
|
||||
this.disabledEvents = {};
|
||||
|
||||
/**
|
||||
* The sequence on WebSocket close
|
||||
* @type {number}
|
||||
*/
|
||||
this.closeSequence = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the WebSocket is expecting to be closed
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.expectingClose = false;
|
||||
for (const event of this.client.options.disabledEvents) this.disabledEvents[event] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the client to be marked as ready and emits the ready event.
|
||||
* @returns {void}
|
||||
*/
|
||||
triggerReady() {
|
||||
if (this.status === Constants.Status.READY) {
|
||||
this.debug('Tried to mark self as ready, but already ready');
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Emitted when the client becomes ready to start working.
|
||||
* @event Client#ready
|
||||
*/
|
||||
this.status = Constants.Status.READY;
|
||||
this.client.emit(Constants.Events.READY);
|
||||
this.packetManager.handleQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the client is ready to be marked as ready.
|
||||
* @returns {void}
|
||||
*/
|
||||
checkIfReady() {
|
||||
if (this.status === Constants.Status.READY || this.status === Constants.Status.NEARLY) return false;
|
||||
let unavailableGuilds = 0;
|
||||
for (const guild of this.client.guilds.values()) {
|
||||
if (!guild.available) unavailableGuilds++;
|
||||
}
|
||||
if (unavailableGuilds === 0) {
|
||||
this.status = Constants.Status.NEARLY;
|
||||
if (!this.client.options.fetchAllMembers) return this.triggerReady();
|
||||
// Fetch all members before marking self as ready
|
||||
const promises = this.client.guilds.map(g => g.fetchMembers());
|
||||
Promise.all(promises)
|
||||
.then(() => this.triggerReady())
|
||||
.catch(e => {
|
||||
this.debug(`Failed to fetch all members before ready! ${e}`);
|
||||
this.triggerReady();
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Util
|
||||
/**
|
||||
* Emits a debug message.
|
||||
* @param {string} message Debug message
|
||||
* @returns {void}
|
||||
*/
|
||||
debug(message) {
|
||||
if (message instanceof Error) message = message.stack;
|
||||
return this.manager.debug(`[connection] ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to serialise data from the WebSocket.
|
||||
* @param {string|Object} data Data to unpack
|
||||
* @returns {Object}
|
||||
*/
|
||||
unpack(data) {
|
||||
if (data instanceof ArrayBuffer) data = Buffer.from(new Uint8Array(data));
|
||||
|
||||
if (erlpack && typeof data !== 'string') return erlpack.unpack(data);
|
||||
else if (data instanceof Buffer) data = zlib.inflateSync(data).toString();
|
||||
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs an object ready to be sent.
|
||||
* @param {Object} data Data to pack
|
||||
* @returns {string|Buffer}
|
||||
*/
|
||||
pack(data) {
|
||||
return erlpack ? erlpack.pack(data) : JSON.stringify(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the current WebSocket queue.
|
||||
*/
|
||||
processQueue() {
|
||||
if (this.ratelimit.remaining === 0) return;
|
||||
if (this.ratelimit.queue.length === 0) return;
|
||||
if (this.ratelimit.remaining === this.ratelimit.total) {
|
||||
this.ratelimit.resetTimer = this.client.setTimeout(() => {
|
||||
this.ratelimit.remaining = this.ratelimit.total;
|
||||
this.processQueue();
|
||||
}, this.ratelimit.time);
|
||||
}
|
||||
while (this.ratelimit.remaining > 0) {
|
||||
const item = this.ratelimit.queue.shift();
|
||||
if (!item) return;
|
||||
this._send(item);
|
||||
this.ratelimit.remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends data, bypassing the queue.
|
||||
* @param {Object} data Packet to send
|
||||
* @returns {void}
|
||||
*/
|
||||
_send(data) {
|
||||
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
||||
this.debug(`Tried to send packet ${data} but no WebSocket is available!`);
|
||||
return;
|
||||
}
|
||||
this.ws.send(this.pack(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data to the queue to be sent.
|
||||
* @param {Object} data Packet to send
|
||||
* @returns {void}
|
||||
*/
|
||||
send(data) {
|
||||
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
||||
this.debug(`Tried to send packet ${data} but no WebSocket is available!`);
|
||||
return;
|
||||
}
|
||||
this.ratelimit.queue.push(data);
|
||||
this.processQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connection to a gateway.
|
||||
* @param {string} gateway The gateway to connect to
|
||||
* @param {number} [after=0] How long to wait before connecting
|
||||
* @param {boolean} [force=false] Whether or not to force a new connection even if one already exists
|
||||
* @returns {boolean}
|
||||
*/
|
||||
connect(gateway = this.gateway, after = 0, force = false) {
|
||||
if (after) return this.client.setTimeout(() => this.connect(gateway, 0, force), after); // eslint-disable-line
|
||||
if (this.ws && !force) {
|
||||
this.debug('WebSocket connection already exists');
|
||||
return false;
|
||||
} else if (typeof gateway !== 'string') {
|
||||
this.debug(`Tried to connect to an invalid gateway: ${gateway}`);
|
||||
return false;
|
||||
}
|
||||
this.expectingClose = false;
|
||||
this.gateway = gateway;
|
||||
this.debug(`Connecting to ${gateway}`);
|
||||
const ws = this.ws = new WebSocket(gateway);
|
||||
if (browser) ws.binaryType = 'arraybuffer';
|
||||
ws.onmessage = this.onMessage.bind(this);
|
||||
ws.onopen = this.onOpen.bind(this);
|
||||
ws.onerror = this.onError.bind(this);
|
||||
ws.onclose = this.onClose.bind(this);
|
||||
this.status = Constants.Status.CONNECTING;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the connection.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
destroy() {
|
||||
const ws = this.ws;
|
||||
if (!ws) {
|
||||
this.debug('Attempted to destroy WebSocket but no connection exists!');
|
||||
return false;
|
||||
}
|
||||
this.heartbeat(-1);
|
||||
this.expectingClose = true;
|
||||
ws.close(1000);
|
||||
this.packetManager.handleQueue();
|
||||
this.ws = null;
|
||||
this.status = Constants.Status.DISCONNECTED;
|
||||
this.ratelimit.remaining = this.ratelimit.total;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a message is received.
|
||||
* @param {Event} event Event received
|
||||
* @returns {boolean}
|
||||
*/
|
||||
onMessage(event) {
|
||||
let data;
|
||||
try {
|
||||
data = this.unpack(event.data);
|
||||
} catch (err) {
|
||||
this.emit('debug', err);
|
||||
}
|
||||
return this.onPacket(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current sequence of the connection.
|
||||
* @param {number} s New sequence
|
||||
*/
|
||||
setSequence(s) {
|
||||
this.sequence = s > this.sequence ? s : this.sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a packet is received.
|
||||
* @param {Object} packet Received packet
|
||||
* @returns {boolean}
|
||||
*/
|
||||
onPacket(packet) {
|
||||
if (!packet) {
|
||||
this.debug('Received null packet');
|
||||
return false;
|
||||
}
|
||||
this.client.emit('raw', packet);
|
||||
switch (packet.op) {
|
||||
case Constants.OPCodes.HELLO:
|
||||
return this.heartbeat(packet.d.heartbeat_interval);
|
||||
case Constants.OPCodes.RECONNECT:
|
||||
return this.reconnect();
|
||||
case Constants.OPCodes.INVALID_SESSION:
|
||||
if (!packet.d) this.sessionID = null;
|
||||
this.sequence = -1;
|
||||
this.debug('Session invalidated -- will identify with a new session');
|
||||
return this.identify(packet.d ? 2500 : 0);
|
||||
case Constants.OPCodes.HEARTBEAT_ACK:
|
||||
return this.ackHeartbeat();
|
||||
case Constants.OPCodes.HEARTBEAT:
|
||||
return this.heartbeat();
|
||||
default:
|
||||
return this.packetManager.handle(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a connection is opened to the gateway.
|
||||
* @param {Event} event Received open event
|
||||
*/
|
||||
onOpen(event) {
|
||||
if (event && event.target && event.target.url) this.gateway = event.target.url;
|
||||
this.debug(`Connected to gateway ${this.gateway}`);
|
||||
this.identify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes a reconnection to the gateway.
|
||||
*/
|
||||
reconnect() {
|
||||
this.debug('Attemping to reconnect in 5500ms...');
|
||||
/**
|
||||
* Emitted whenever the client tries to reconnect to the WebSocket.
|
||||
* @event Client#reconnecting
|
||||
*/
|
||||
this.client.emit(Constants.Events.RECONNECTING);
|
||||
this.connect(this.gateway, 5500, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever an error occurs with the WebSocket.
|
||||
* @param {Error} error The error that occurred
|
||||
*/
|
||||
onError(error) {
|
||||
if (error && error.message === 'uWs client connection error') {
|
||||
this.reconnect();
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Emitted whenever the client's WebSocket encounters a connection error.
|
||||
* @event Client#error
|
||||
* @param {Error} error The encountered error
|
||||
*/
|
||||
this.client.emit(Constants.Events.ERROR, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @external CloseEvent
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called whenever a connection to the gateway is closed.
|
||||
* @param {CloseEvent} event Close event that was received
|
||||
*/
|
||||
onClose(event) {
|
||||
this.debug(`${this.expectingClose ? 'Client' : 'Server'} closed the WebSocket connection: ${event.code}`);
|
||||
this.closeSequence = this.sequence;
|
||||
// Reset the state before trying to fix anything
|
||||
this.emit('close', event);
|
||||
this.heartbeat(-1);
|
||||
// Should we reconnect?
|
||||
if (event.code === 1000 ? this.expectingClose : Constants.WSCodes[event.code]) {
|
||||
this.expectingClose = false;
|
||||
/**
|
||||
* Emitted when the client's WebSocket disconnects and will no longer attempt to reconnect.
|
||||
* @event Client#disconnect
|
||||
* @param {CloseEvent} event The WebSocket close event
|
||||
*/
|
||||
this.client.emit(Constants.Events.DISCONNECT, event);
|
||||
this.debug(Constants.WSCodes[event.code]);
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
this.expectingClose = false;
|
||||
this.reconnect();
|
||||
}
|
||||
|
||||
// Heartbeat
|
||||
/**
|
||||
* Acknowledges a heartbeat.
|
||||
*/
|
||||
ackHeartbeat() {
|
||||
this.debug(`Heartbeat acknowledged, latency of ${Date.now() - this.lastPingTimestamp}ms`);
|
||||
this.client._pong(this.lastPingTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a heartbeat or sets an interval for sending heartbeats.
|
||||
* @param {number} [time] If -1, clears the interval, any other number sets an interval
|
||||
* If no value is given, a heartbeat will be sent instantly
|
||||
*/
|
||||
heartbeat(time) {
|
||||
if (!isNaN(time)) {
|
||||
if (time === -1) {
|
||||
this.debug('Clearing heartbeat interval');
|
||||
this.client.clearInterval(this.heartbeatInterval);
|
||||
this.heartbeatInterval = null;
|
||||
} else {
|
||||
this.debug(`Setting a heartbeat interval for ${time}ms`);
|
||||
this.heartbeatInterval = this.client.setInterval(() => this.heartbeat(), time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.debug('Sending a heartbeat');
|
||||
this.lastPingTimestamp = Date.now();
|
||||
this.send({
|
||||
op: Constants.OPCodes.HEARTBEAT,
|
||||
d: this.sequence,
|
||||
});
|
||||
}
|
||||
|
||||
// Identification
|
||||
/**
|
||||
* Identifies the client on a connection.
|
||||
* @param {number} [after] How long to wait before identifying
|
||||
* @returns {void}
|
||||
*/
|
||||
identify(after) {
|
||||
if (after) return this.client.setTimeout(this.identify.bind(this), after);
|
||||
return this.sessionID ? this.identifyResume() : this.identifyNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies as a new connection on the gateway.
|
||||
* @returns {void}
|
||||
*/
|
||||
identifyNew() {
|
||||
if (!this.client.token) {
|
||||
this.debug('No token available to identify a new session with');
|
||||
return;
|
||||
}
|
||||
// Clone the generic payload and assign the token
|
||||
const d = Object.assign({ token: this.client.token }, this.client.options.ws);
|
||||
|
||||
// Sharding stuff
|
||||
const { shardId, shardCount } = this.client.options;
|
||||
if (shardCount > 0) d.shard = [Number(shardId), Number(shardCount)];
|
||||
|
||||
// Send the payload
|
||||
this.debug('Identifying as a new session');
|
||||
this.send({ op: Constants.OPCodes.IDENTIFY, d });
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a session on the gateway.
|
||||
* @returns {void}
|
||||
*/
|
||||
identifyResume() {
|
||||
if (!this.sessionID) {
|
||||
this.debug('Warning: wanted to resume but session ID not available; identifying as a new session instead');
|
||||
return this.identifyNew();
|
||||
}
|
||||
this.debug(`Attempting to resume session ${this.sessionID}`);
|
||||
|
||||
const d = {
|
||||
token: this.client.token,
|
||||
session_id: this.sessionID,
|
||||
seq: this.sequence,
|
||||
};
|
||||
|
||||
return this.send({
|
||||
op: Constants.OPCodes.RESUME,
|
||||
d,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoding the WebSocket connections will use.
|
||||
* @type {string}
|
||||
*/
|
||||
WebSocketConnection.ENCODING = erlpack ? 'etf' : 'json';
|
||||
WebSocketConnection.WebSocket = WebSocket;
|
||||
|
||||
module.exports = WebSocketConnection;
|
||||
90
node_modules/discord.js/src/client/websocket/WebSocketManager.js
generated
vendored
Normal file
90
node_modules/discord.js/src/client/websocket/WebSocketManager.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Constants = require('../../util/Constants');
|
||||
const WebSocketConnection = require('./WebSocketConnection');
|
||||
|
||||
/**
|
||||
* WebSocket Manager of the client.
|
||||
* @private
|
||||
*/
|
||||
class WebSocketManager extends EventEmitter {
|
||||
constructor(client) {
|
||||
super();
|
||||
/**
|
||||
* The client that instantiated this WebSocketManager
|
||||
* @type {Client}
|
||||
*/
|
||||
this.client = client;
|
||||
|
||||
/**
|
||||
* The WebSocket connection of this manager
|
||||
* @type {?WebSocketConnection}
|
||||
*/
|
||||
this.connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a heartbeat on the available connection.
|
||||
* @returns {void}
|
||||
*/
|
||||
heartbeat() {
|
||||
if (!this.connection) return this.debug('No connection to heartbeat');
|
||||
return this.connection.heartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a debug event.
|
||||
* @param {string} message Debug message
|
||||
* @returns {void}
|
||||
*/
|
||||
debug(message) {
|
||||
return this.client.emit('debug', `[ws] ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the client.
|
||||
* @returns {void} Whether or not destruction was successful
|
||||
*/
|
||||
destroy() {
|
||||
if (!this.connection) {
|
||||
this.debug('Attempted to destroy WebSocket but no connection exists!');
|
||||
return false;
|
||||
}
|
||||
return this.connection.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet on the available WebSocket.
|
||||
* @param {Object} packet Packet to send
|
||||
* @returns {void}
|
||||
*/
|
||||
send(packet) {
|
||||
if (!this.connection) {
|
||||
this.debug('No connection to websocket');
|
||||
return;
|
||||
}
|
||||
this.connection.send(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the client to a gateway.
|
||||
* @param {string} gateway The gateway to connect to
|
||||
* @returns {boolean}
|
||||
*/
|
||||
connect(gateway) {
|
||||
if (!this.connection) {
|
||||
this.connection = new WebSocketConnection(this, gateway);
|
||||
return true;
|
||||
}
|
||||
switch (this.connection.status) {
|
||||
case Constants.Status.IDLE:
|
||||
case Constants.Status.DISCONNECTED:
|
||||
this.connection.connect(gateway, 5500);
|
||||
return true;
|
||||
default:
|
||||
this.debug(`Couldn't connect to ${gateway} as the websocket is at state ${this.connection.status}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebSocketManager;
|
||||
108
node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
generated
vendored
Normal file
108
node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
const Constants = require('../../../util/Constants');
|
||||
|
||||
const BeforeReadyWhitelist = [
|
||||
Constants.WSEvents.READY,
|
||||
Constants.WSEvents.RESUMED,
|
||||
Constants.WSEvents.GUILD_CREATE,
|
||||
Constants.WSEvents.GUILD_DELETE,
|
||||
Constants.WSEvents.GUILD_MEMBERS_CHUNK,
|
||||
Constants.WSEvents.GUILD_MEMBER_ADD,
|
||||
Constants.WSEvents.GUILD_MEMBER_REMOVE,
|
||||
];
|
||||
|
||||
class WebSocketPacketManager {
|
||||
constructor(connection) {
|
||||
this.ws = connection;
|
||||
this.handlers = {};
|
||||
this.queue = [];
|
||||
|
||||
this.register(Constants.WSEvents.READY, require('./handlers/Ready'));
|
||||
this.register(Constants.WSEvents.RESUMED, require('./handlers/Resumed'));
|
||||
this.register(Constants.WSEvents.GUILD_CREATE, require('./handlers/GuildCreate'));
|
||||
this.register(Constants.WSEvents.GUILD_DELETE, require('./handlers/GuildDelete'));
|
||||
this.register(Constants.WSEvents.GUILD_UPDATE, require('./handlers/GuildUpdate'));
|
||||
this.register(Constants.WSEvents.GUILD_BAN_ADD, require('./handlers/GuildBanAdd'));
|
||||
this.register(Constants.WSEvents.GUILD_BAN_REMOVE, require('./handlers/GuildBanRemove'));
|
||||
this.register(Constants.WSEvents.GUILD_MEMBER_ADD, require('./handlers/GuildMemberAdd'));
|
||||
this.register(Constants.WSEvents.GUILD_MEMBER_REMOVE, require('./handlers/GuildMemberRemove'));
|
||||
this.register(Constants.WSEvents.GUILD_MEMBER_UPDATE, require('./handlers/GuildMemberUpdate'));
|
||||
this.register(Constants.WSEvents.GUILD_ROLE_CREATE, require('./handlers/GuildRoleCreate'));
|
||||
this.register(Constants.WSEvents.GUILD_ROLE_DELETE, require('./handlers/GuildRoleDelete'));
|
||||
this.register(Constants.WSEvents.GUILD_ROLE_UPDATE, require('./handlers/GuildRoleUpdate'));
|
||||
this.register(Constants.WSEvents.GUILD_EMOJIS_UPDATE, require('./handlers/GuildEmojisUpdate'));
|
||||
this.register(Constants.WSEvents.GUILD_MEMBERS_CHUNK, require('./handlers/GuildMembersChunk'));
|
||||
this.register(Constants.WSEvents.CHANNEL_CREATE, require('./handlers/ChannelCreate'));
|
||||
this.register(Constants.WSEvents.CHANNEL_DELETE, require('./handlers/ChannelDelete'));
|
||||
this.register(Constants.WSEvents.CHANNEL_UPDATE, require('./handlers/ChannelUpdate'));
|
||||
this.register(Constants.WSEvents.CHANNEL_PINS_UPDATE, require('./handlers/ChannelPinsUpdate'));
|
||||
this.register(Constants.WSEvents.PRESENCE_UPDATE, require('./handlers/PresenceUpdate'));
|
||||
this.register(Constants.WSEvents.USER_UPDATE, require('./handlers/UserUpdate'));
|
||||
this.register(Constants.WSEvents.USER_NOTE_UPDATE, require('./handlers/UserNoteUpdate'));
|
||||
this.register(Constants.WSEvents.USER_SETTINGS_UPDATE, require('./handlers/UserSettingsUpdate'));
|
||||
this.register(Constants.WSEvents.USER_GUILD_SETTINGS_UPDATE, require('./handlers/UserGuildSettingsUpdate'));
|
||||
this.register(Constants.WSEvents.VOICE_STATE_UPDATE, require('./handlers/VoiceStateUpdate'));
|
||||
this.register(Constants.WSEvents.TYPING_START, require('./handlers/TypingStart'));
|
||||
this.register(Constants.WSEvents.MESSAGE_CREATE, require('./handlers/MessageCreate'));
|
||||
this.register(Constants.WSEvents.MESSAGE_DELETE, require('./handlers/MessageDelete'));
|
||||
this.register(Constants.WSEvents.MESSAGE_UPDATE, require('./handlers/MessageUpdate'));
|
||||
this.register(Constants.WSEvents.MESSAGE_DELETE_BULK, require('./handlers/MessageDeleteBulk'));
|
||||
this.register(Constants.WSEvents.VOICE_SERVER_UPDATE, require('./handlers/VoiceServerUpdate'));
|
||||
this.register(Constants.WSEvents.GUILD_SYNC, require('./handlers/GuildSync'));
|
||||
this.register(Constants.WSEvents.RELATIONSHIP_ADD, require('./handlers/RelationshipAdd'));
|
||||
this.register(Constants.WSEvents.RELATIONSHIP_REMOVE, require('./handlers/RelationshipRemove'));
|
||||
this.register(Constants.WSEvents.MESSAGE_REACTION_ADD, require('./handlers/MessageReactionAdd'));
|
||||
this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE, require('./handlers/MessageReactionRemove'));
|
||||
this.register(Constants.WSEvents.MESSAGE_REACTION_REMOVE_ALL, require('./handlers/MessageReactionRemoveAll'));
|
||||
}
|
||||
|
||||
get client() {
|
||||
return this.ws.client;
|
||||
}
|
||||
|
||||
register(event, Handler) {
|
||||
this.handlers[event] = new Handler(this);
|
||||
}
|
||||
|
||||
handleQueue() {
|
||||
this.queue.forEach((element, index) => {
|
||||
this.handle(this.queue[index], true);
|
||||
this.queue.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
handle(packet, queue = false) {
|
||||
if (packet.op === Constants.OPCodes.HEARTBEAT_ACK) {
|
||||
this.ws.client._pong(this.ws.client._pingTimestamp);
|
||||
this.ws.lastHeartbeatAck = true;
|
||||
this.ws.client.emit('debug', 'Heartbeat acknowledged');
|
||||
} else if (packet.op === Constants.OPCodes.HEARTBEAT) {
|
||||
this.client.ws.send({
|
||||
op: Constants.OPCodes.HEARTBEAT,
|
||||
d: this.client.ws.sequence,
|
||||
});
|
||||
this.ws.client.emit('debug', 'Received gateway heartbeat');
|
||||
}
|
||||
|
||||
if (this.ws.status === Constants.Status.RECONNECTING) {
|
||||
this.ws.reconnecting = false;
|
||||
this.ws.checkIfReady();
|
||||
}
|
||||
|
||||
this.ws.setSequence(packet.s);
|
||||
|
||||
if (this.ws.disabledEvents[packet.t] !== undefined) return false;
|
||||
|
||||
if (this.ws.status !== Constants.Status.READY) {
|
||||
if (BeforeReadyWhitelist.indexOf(packet.t) === -1) {
|
||||
this.queue.push(packet);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue && this.queue.length > 0) this.handleQueue();
|
||||
if (this.handlers[packet.t]) return this.handlers[packet.t].handle(packet);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebSocketPacketManager;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/AbstractHandler.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
class AbstractHandler {
|
||||
constructor(packetManager) {
|
||||
this.packetManager = packetManager;
|
||||
}
|
||||
|
||||
handle(packet) {
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AbstractHandler;
|
||||
17
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelCreate.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class ChannelCreateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.ChannelCreate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a channel is created.
|
||||
* @event Client#channelCreate
|
||||
* @param {Channel} channel The channel that was created
|
||||
*/
|
||||
|
||||
module.exports = ChannelCreateHandler;
|
||||
20
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
generated
vendored
Normal file
20
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelDelete.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class ChannelDeleteHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a channel is deleted.
|
||||
* @event Client#channelDelete
|
||||
* @param {Channel} channel The channel that was deleted
|
||||
*/
|
||||
|
||||
module.exports = ChannelDeleteHandler;
|
||||
31
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
generated
vendored
Normal file
31
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelPinsUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
/*
|
||||
{ t: 'CHANNEL_PINS_UPDATE',
|
||||
s: 666,
|
||||
op: 0,
|
||||
d:
|
||||
{ last_pin_timestamp: '2016-08-28T17:37:13.171774+00:00',
|
||||
channel_id: '314866471639044027' } }
|
||||
*/
|
||||
|
||||
class ChannelPinsUpdate extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/ChannelUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class ChannelUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.ChannelUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ChannelUpdateHandler;
|
||||
23
node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
generated
vendored
Normal file
23
node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanAdd.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// ##untested handler##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class GuildBanAddHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
20
node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
generated
vendored
Normal file
20
node_modules/discord.js/src/client/websocket/packets/handlers/GuildBanRemove.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// ##untested handler##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildBanRemoveHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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
|
||||
*/
|
||||
|
||||
module.exports = GuildBanRemoveHandler;
|
||||
22
node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
generated
vendored
Normal file
22
node_modules/discord.js/src/client/websocket/packets/handlers/GuildCreate.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildCreateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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
|
||||
guild.setup(data);
|
||||
this.packetManager.ws.checkIfReady();
|
||||
}
|
||||
} else {
|
||||
// A new guild
|
||||
client.dataManager.newGuild(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildCreateHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/GuildDelete.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class GuildDeleteHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild is deleted/left.
|
||||
* @event Client#guildDelete
|
||||
* @param {Guild} guild The guild that was deleted
|
||||
*/
|
||||
|
||||
module.exports = GuildDeleteHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildEmojisUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildEmojisUpdate extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildEmojisUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildEmojisUpdate;
|
||||
17
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberAdd.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// ##untested handler##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildMemberAddHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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;
|
||||
13
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
generated
vendored
Normal file
13
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberRemove.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// ##untested handler##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildMemberRemoveHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildMemberRemove.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildMemberRemoveHandler;
|
||||
18
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
generated
vendored
Normal file
18
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMemberUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// ##untested handler##
|
||||
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildMemberUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildMemberUpdateHandler;
|
||||
33
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
generated
vendored
Normal file
33
node_modules/discord.js/src/client/websocket/packets/handlers/GuildMembersChunk.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
// Uncomment in v12
|
||||
// const Collection = require('../../../../util/Collection');
|
||||
|
||||
class GuildMembersChunkHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
const guild = client.guilds.get(data.guild_id);
|
||||
if (!guild) return;
|
||||
|
||||
// Uncomment in v12
|
||||
// const members = new Collection();
|
||||
//
|
||||
// for (const member of data.members) members.set(member.id, guild._addMember(member, false));
|
||||
|
||||
const members = data.members.map(member => guild._addMember(member, false));
|
||||
|
||||
client.emit(Constants.Events.GUILD_MEMBERS_CHUNK, members, guild);
|
||||
|
||||
client.ws.lastHeartbeatAck = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a chunk of guild members is received (all members come from the same guild).
|
||||
* @event Client#guildMembersChunk
|
||||
* @param {GuildMember[]} members The members in the chunk
|
||||
* @param {Guild} guild The guild related to the member chunk
|
||||
*/
|
||||
|
||||
module.exports = GuildMembersChunkHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleCreate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildRoleCreateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildRoleCreate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildRoleCreateHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleDelete.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildRoleDeleteHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildRoleDelete.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildRoleDeleteHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildRoleUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildRoleUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildRoleUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildRoleUpdateHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildSync.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildSyncHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildSync.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildSyncHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/GuildUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class GuildUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.GuildUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildUpdateHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class MessageCreateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a message is created.
|
||||
* @event Client#message
|
||||
* @param {Message} message The created message
|
||||
*/
|
||||
|
||||
module.exports = MessageCreateHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/MessageDelete.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class MessageDeleteHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a message is deleted.
|
||||
* @event Client#messageDelete
|
||||
* @param {Message} message The deleted message
|
||||
*/
|
||||
|
||||
module.exports = MessageDeleteHandler;
|
||||
17
node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
generated
vendored
Normal file
17
node_modules/discord.js/src/client/websocket/packets/handlers/MessageDeleteBulk.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class MessageDeleteBulkHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.MessageDeleteBulk.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever messages are deleted in bulk.
|
||||
* @event Client#messageDeleteBulk
|
||||
* @param {Collection<Snowflake, Message>} messages The deleted messages, mapped by their ID
|
||||
*/
|
||||
|
||||
module.exports = MessageDeleteBulkHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionAdd.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class MessageReactionAddHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.MessageReactionAdd.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageReactionAddHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemove.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class MessageReactionRemove extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.MessageReactionRemove.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageReactionRemove;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class MessageReactionRemoveAll extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.MessageReactionRemoveAll.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageReactionRemoveAll;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/MessageUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class MessageUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.MessageUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MessageUpdateHandler;
|
||||
76
node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
generated
vendored
Normal file
76
node_modules/discord.js/src/client/websocket/packets/handlers/PresenceUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
const Util = require('../../../../util/Util');
|
||||
|
||||
class PresenceUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
|
||||
// Step 1
|
||||
if (!user) {
|
||||
if (data.user.username) {
|
||||
user = client.dataManager.newUser(data.user);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const oldUser = Util.cloneObject(user);
|
||||
user.patch(data.user);
|
||||
if (!user.equals(oldUser)) {
|
||||
client.emit(Constants.Events.USER_UPDATE, oldUser, user);
|
||||
}
|
||||
|
||||
if (guild) {
|
||||
let member = guild.members.get(user.id);
|
||||
if (!member && data.status !== 'offline') {
|
||||
member = guild._addMember({
|
||||
user,
|
||||
roles: data.roles,
|
||||
deaf: false,
|
||||
mute: false,
|
||||
}, false);
|
||||
client.emit(Constants.Events.GUILD_MEMBER_AVAILABLE, member);
|
||||
}
|
||||
if (member) {
|
||||
if (client.listenerCount(Constants.Events.PRESENCE_UPDATE) === 0) {
|
||||
guild._setPresence(user.id, data);
|
||||
return;
|
||||
}
|
||||
const oldMember = Util.cloneObject(member);
|
||||
if (member.presence) {
|
||||
oldMember.frozenPresence = Util.cloneObject(member.presence);
|
||||
}
|
||||
guild._setPresence(user.id, data);
|
||||
client.emit(Constants.Events.PRESENCE_UPDATE, oldMember, member);
|
||||
} else {
|
||||
guild._setPresence(user.id, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild member's presence changes, or they change one of their details.
|
||||
* @event Client#presenceUpdate
|
||||
* @param {GuildMember} oldMember The member before the presence update
|
||||
* @param {GuildMember} newMember The member after the presence update
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted whenever a user's details (e.g. username) are changed.
|
||||
* @event Client#userUpdate
|
||||
* @param {User} oldUser The user before the update
|
||||
* @param {User} newUser The user after the update
|
||||
*/
|
||||
|
||||
/**
|
||||
* Emitted whenever a member becomes available in a large guild.
|
||||
* @event Client#guildMemberAvailable
|
||||
* @param {GuildMember} member The member that became available
|
||||
*/
|
||||
|
||||
module.exports = PresenceUpdateHandler;
|
||||
83
node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
generated
vendored
Normal file
83
node_modules/discord.js/src/client/websocket/packets/handlers/Ready.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
const ClientUser = require('../../../../structures/ClientUser');
|
||||
|
||||
class ReadyHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
|
||||
client.ws.heartbeat();
|
||||
|
||||
data.user.user_settings = data.user_settings;
|
||||
data.user.user_guild_settings = data.user_guild_settings;
|
||||
|
||||
const clientUser = new ClientUser(client, data.user);
|
||||
client.user = clientUser;
|
||||
client.readyAt = new Date();
|
||||
client.users.set(clientUser.id, clientUser);
|
||||
|
||||
for (const guild of data.guilds) if (!client.guilds.has(guild.id)) client.dataManager.newGuild(guild);
|
||||
for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM);
|
||||
|
||||
for (const relation of data.relationships) {
|
||||
const user = client.dataManager.newUser(relation.user);
|
||||
if (relation.type === 1) {
|
||||
client.user.friends.set(user.id, user);
|
||||
} else if (relation.type === 2) {
|
||||
client.user.blocked.set(user.id, user);
|
||||
}
|
||||
}
|
||||
|
||||
data.presences = data.presences || [];
|
||||
for (const presence of data.presences) {
|
||||
client.dataManager.newUser(presence.user);
|
||||
client._setPresence(presence.user.id, presence);
|
||||
}
|
||||
|
||||
if (data.notes) {
|
||||
for (const user in data.notes) {
|
||||
let note = data.notes[user];
|
||||
if (!note.length) note = null;
|
||||
|
||||
client.user.notes.set(user, note);
|
||||
}
|
||||
}
|
||||
|
||||
if (!client.user.bot && client.options.sync) client.setInterval(client.syncGuilds.bind(client), 30000);
|
||||
|
||||
if (!client.users.has('1')) {
|
||||
client.dataManager.newUser({
|
||||
id: '1',
|
||||
username: 'Clyde',
|
||||
discriminator: '0000',
|
||||
avatar: 'https://discordapp.com/assets/f78426a064bc9dd24847519259bc42af.png',
|
||||
bot: true,
|
||||
status: 'online',
|
||||
game: null,
|
||||
verified: true,
|
||||
});
|
||||
}
|
||||
|
||||
const t = client.setTimeout(() => {
|
||||
client.ws.connection.triggerReady();
|
||||
}, 1200 * data.guilds.length);
|
||||
|
||||
client.setMaxListeners(data.guilds.length + 10);
|
||||
|
||||
client.once('ready', () => {
|
||||
client.syncGuilds();
|
||||
client.setMaxListeners(10);
|
||||
client.clearTimeout(t);
|
||||
});
|
||||
|
||||
const ws = this.packetManager.ws;
|
||||
|
||||
ws.sessionID = data.session_id;
|
||||
ws._trace = data._trace;
|
||||
client.emit('debug', `READY ${ws._trace.join(' -> ')} ${ws.sessionID}`);
|
||||
ws.checkIfReady();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReadyHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipAdd.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class RelationshipAddHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
if (data.type === 1) {
|
||||
client.fetchUser(data.id).then(user => {
|
||||
client.user.friends.set(user.id, user);
|
||||
});
|
||||
} else if (data.type === 2) {
|
||||
client.fetchUser(data.id).then(user => {
|
||||
client.user.blocked.set(user.id, user);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RelationshipAddHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/RelationshipRemove.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class RelationshipRemoveHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
if (data.type === 2) {
|
||||
if (client.user.blocked.has(data.id)) {
|
||||
client.user.blocked.delete(data.id);
|
||||
}
|
||||
} else if (data.type === 1) {
|
||||
if (client.user.friends.has(data.id)) {
|
||||
client.user.friends.delete(data.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RelationshipRemoveHandler;
|
||||
28
node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js
generated
vendored
Normal file
28
node_modules/discord.js/src/client/websocket/packets/handlers/Resumed.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class ResumedHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const ws = client.ws.connection;
|
||||
|
||||
ws._trace = packet.d._trace;
|
||||
|
||||
ws.status = Constants.Status.READY;
|
||||
this.packetManager.handleQueue();
|
||||
|
||||
const replayed = ws.sequence - ws.closeSequence;
|
||||
|
||||
ws.debug(`RESUMED ${ws._trace.join(' -> ')} | replayed ${replayed} events.`);
|
||||
client.emit(Constants.Events.RESUME, replayed);
|
||||
ws.heartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever a WebSocket resumes.
|
||||
* @event Client#resume
|
||||
* @param {number} replayed The number of events that were replayed
|
||||
*/
|
||||
|
||||
module.exports = ResumedHandler;
|
||||
68
node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
generated
vendored
Normal file
68
node_modules/discord.js/src/client/websocket/packets/handlers/TypingStart.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class TypingStartHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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);
|
||||
|
||||
if (channel && user) {
|
||||
if (channel.type === 'voice') {
|
||||
client.emit(Constants.Events.WARN, `Discord sent a typing packet to voice channel ${channel.id}`);
|
||||
return;
|
||||
}
|
||||
if (channel._typing.has(user.id)) {
|
||||
const typing = channel._typing.get(user.id);
|
||||
typing.lastTimestamp = timestamp;
|
||||
typing.resetTimeout(tooLate(channel, user));
|
||||
} else {
|
||||
channel._typing.set(user.id, new TypingData(client, timestamp, timestamp, tooLate(channel, user)));
|
||||
client.emit(Constants.Events.TYPING_START, channel, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypingData {
|
||||
constructor(client, since, lastTimestamp, _timeout) {
|
||||
this.client = client;
|
||||
this.since = since;
|
||||
this.lastTimestamp = lastTimestamp;
|
||||
this._timeout = _timeout;
|
||||
}
|
||||
|
||||
resetTimeout(_timeout) {
|
||||
this.client.clearTimeout(this._timeout);
|
||||
this._timeout = _timeout;
|
||||
}
|
||||
|
||||
get elapsedTime() {
|
||||
return Date.now() - this.since;
|
||||
}
|
||||
}
|
||||
|
||||
function tooLate(channel, user) {
|
||||
return channel.client.setTimeout(() => {
|
||||
channel.client.emit(Constants.Events.TYPING_STOP, channel, user, channel._typing.get(user.id));
|
||||
channel._typing.delete(user.id);
|
||||
}, 6000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
module.exports = TypingStartHandler;
|
||||
21
node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js
generated
vendored
Normal file
21
node_modules/discord.js/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
const ClientUserGuildSettings = require('../../../../structures/ClientUserGuildSettings');
|
||||
|
||||
class UserGuildSettingsUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const settings = client.user.guildSettings.get(packet.d.guild_id);
|
||||
if (settings) settings.patch(packet.d);
|
||||
else client.user.guildSettings.set(packet.d.guild_id, new ClientUserGuildSettings(packet.d, client));
|
||||
client.emit(Constants.Events.USER_GUILD_SETTINGS_UPDATE, client.user.guildSettings.get(packet.d.guild_id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever the client user's settings update.
|
||||
* @event Client#clientUserGuildSettingsUpdate
|
||||
* @param {ClientUserGuildSettings} clientUserGuildSettings The new client user guild settings
|
||||
*/
|
||||
|
||||
module.exports = UserGuildSettingsUpdateHandler;
|
||||
12
node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
generated
vendored
Normal file
12
node_modules/discord.js/src/client/websocket/packets/handlers/UserNoteUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class UserNoteUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
|
||||
client.actions.UserNoteUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserNoteUpdateHandler;
|
||||
18
node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js
generated
vendored
Normal file
18
node_modules/discord.js/src/client/websocket/packets/handlers/UserSettingsUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
const Constants = require('../../../../util/Constants');
|
||||
|
||||
class UserSettingsUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
client.user.settings.patch(packet.d);
|
||||
client.emit(Constants.Events.USER_SETTINGS_UPDATE, client.user.settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted when the client user's settings update.
|
||||
* @event Client#clientUserSettingsUpdate
|
||||
* @param {ClientUserSettings} clientUserSettings The new client user settings
|
||||
*/
|
||||
|
||||
module.exports = UserSettingsUpdateHandler;
|
||||
11
node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
generated
vendored
Normal file
11
node_modules/discord.js/src/client/websocket/packets/handlers/UserUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
class UserUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.actions.UserUpdate.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserUpdateHandler;
|
||||
19
node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
generated
vendored
Normal file
19
node_modules/discord.js/src/client/websocket/packets/handlers/VoiceServerUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
/*
|
||||
{
|
||||
"token": "my_token",
|
||||
"guild_id": "41771983423143937",
|
||||
"endpoint": "smart.loyal.discord.gg"
|
||||
}
|
||||
*/
|
||||
|
||||
class VoiceServerUpdate extends AbstractHandler {
|
||||
handle(packet) {
|
||||
const client = this.packetManager.client;
|
||||
const data = packet.d;
|
||||
client.emit('self.voiceServer', data);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceServerUpdate;
|
||||
52
node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
generated
vendored
Normal file
52
node_modules/discord.js/src/client/websocket/packets/handlers/VoiceStateUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
const AbstractHandler = require('./AbstractHandler');
|
||||
|
||||
const Constants = require('../../../../util/Constants');
|
||||
const Util = require('../../../../util/Util');
|
||||
|
||||
class VoiceStateUpdateHandler extends AbstractHandler {
|
||||
handle(packet) {
|
||||
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) {
|
||||
const oldVoiceChannelMember = Util.cloneObject(member);
|
||||
if (member.voiceChannel && member.voiceChannel.id !== data.channel_id) {
|
||||
member.voiceChannel.members.delete(oldVoiceChannelMember.id);
|
||||
}
|
||||
|
||||
// If the member left the voice channel, unset their speaking property
|
||||
if (!data.channel_id) member.speaking = null;
|
||||
|
||||
if (member.user.id === client.user.id && data.channel_id) {
|
||||
client.emit('self.voiceStateUpdate', data);
|
||||
}
|
||||
|
||||
const newChannel = client.channels.get(data.channel_id);
|
||||
if (newChannel) {
|
||||
newChannel.members.set(member.id, member);
|
||||
member.guild.channels.set(data.channel_id, newChannel);
|
||||
}
|
||||
|
||||
member.serverMute = data.mute;
|
||||
member.serverDeaf = data.deaf;
|
||||
member.selfMute = data.self_mute;
|
||||
member.selfDeaf = data.self_deaf;
|
||||
member.voiceSessionID = data.session_id;
|
||||
member.voiceChannelID = data.channel_id;
|
||||
client.emit(Constants.Events.VOICE_STATE_UPDATE, oldVoiceChannelMember, member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user