mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-11 00:53:31 +01:00
Merge remote-tracking branch 'origin/indev' into indev-voice
This commit is contained in:
@@ -20,22 +20,19 @@ class Client extends EventEmitter {
|
||||
/**
|
||||
* @param {ClientOptions} [options] Options for the client
|
||||
*/
|
||||
constructor(options) {
|
||||
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 = mergeDefault(Constants.DefaultOptions, options);
|
||||
|
||||
if (!this.options.shardId && 'SHARD_ID' in process.env) {
|
||||
this.options.shardId = Number(process.env.SHARD_ID);
|
||||
}
|
||||
|
||||
if (!this.options.shardCount && 'SHARD_COUNT' in process.env) {
|
||||
this.options.shardCount = Number(process.env.SHARD_COUNT);
|
||||
}
|
||||
this._validateOptions();
|
||||
|
||||
/**
|
||||
* The REST manager of the client
|
||||
@@ -117,11 +114,15 @@ class Client extends EventEmitter {
|
||||
*/
|
||||
this.presences = new Collection();
|
||||
|
||||
/**
|
||||
* The authorization token for the logged in user/bot.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.token = null;
|
||||
if (!this.token && 'CLIENT_TOKEN' in process.env) {
|
||||
/**
|
||||
* The authorization token for the logged in user/bot.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.token = process.env.CLIENT_TOKEN;
|
||||
} else {
|
||||
this.token = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The email, if there is one, for the logged in Client
|
||||
@@ -145,7 +146,7 @@ class Client extends EventEmitter {
|
||||
* The date at which the Client was regarded as being in the `READY` state.
|
||||
* @type {?Date}
|
||||
*/
|
||||
this.readyTime = null;
|
||||
this.readyAt = null;
|
||||
|
||||
this._timeouts = new Set();
|
||||
this._intervals = new Set();
|
||||
@@ -170,7 +171,7 @@ class Client extends EventEmitter {
|
||||
* @readonly
|
||||
*/
|
||||
get uptime() {
|
||||
return this.readyTime ? Date.now() - this.readyTime : null;
|
||||
return this.readyAt ? Date.now() - this.readyAt : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,6 +196,15 @@ class Client extends EventEmitter {
|
||||
return emojis;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp that the client was last ready at
|
||||
* @type {?number}
|
||||
* @readonly
|
||||
*/
|
||||
get readyTimestamp() {
|
||||
return this.readyAt ? this.readyAt.getTime() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the client in. If successful, resolves with the account's token. <warn>If you're making a bot, it's
|
||||
* much better to use a bot account rather than a user account.
|
||||
@@ -241,13 +251,13 @@ class Client extends EventEmitter {
|
||||
/**
|
||||
* This shouldn't really be necessary to most developers as it is automatically invoked every 30 seconds, however
|
||||
* if you wish to force a sync of Guild data, you can use this. Only applicable to user accounts.
|
||||
* @param {Guild[]} [guilds=this.guilds.array()] An array of guilds to sync
|
||||
* @param {Guild[]|Collection<string, Guild>} [guilds=this.guilds] An array or collection of guilds to sync
|
||||
*/
|
||||
syncGuilds(guilds = this.guilds.array()) {
|
||||
syncGuilds(guilds = this.guilds) {
|
||||
if (!this.user.bot) {
|
||||
this.ws.send({
|
||||
op: 12,
|
||||
d: guilds.map(g => g.id),
|
||||
d: guilds instanceof Collection ? guilds.keyArray() : guilds.map(g => g.id),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -265,13 +275,23 @@ class Client extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Fetches an invite object from an invite code.
|
||||
* @param {string} code the invite code.
|
||||
* @param {InviteResolvable} invite An invite code or URL
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
fetchInvite(code) {
|
||||
fetchInvite(invite) {
|
||||
const code = this.resolver.resolveInviteCode(invite);
|
||||
return this.rest.methods.getInvite(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a webhook by ID.
|
||||
* @param {string} id ID of the webhook
|
||||
* @returns {Promise<Webhook>}
|
||||
*/
|
||||
fetchWebhook(id) {
|
||||
return this.rest.methods.getWebhook(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeps all 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.
|
||||
@@ -281,7 +301,7 @@ class Client extends EventEmitter {
|
||||
* or -1 if the message cache lifetime is unlimited
|
||||
*/
|
||||
sweepMessages(lifetime = this.options.messageCacheLifetime) {
|
||||
if (typeof lifetime !== 'number' || isNaN(lifetime)) throw new TypeError('Lifetime must be a number.');
|
||||
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;
|
||||
@@ -344,6 +364,39 @@ class Client extends EventEmitter {
|
||||
_eval(script) {
|
||||
return eval(script);
|
||||
}
|
||||
|
||||
_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;
|
||||
|
||||
@@ -118,6 +118,12 @@ class ClientDataManager {
|
||||
updateChannel(currentChannel, newData) {
|
||||
currentChannel.setup(newData);
|
||||
}
|
||||
|
||||
updateEmoji(currentEmoji, newData) {
|
||||
const oldEmoji = cloneObject(currentEmoji);
|
||||
currentEmoji.setup(newData);
|
||||
this.client.emit(Constants.Events.GUILD_EMOJI_UPDATE, oldEmoji, currentEmoji);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientDataManager;
|
||||
|
||||
@@ -99,23 +99,6 @@ class ClientDataResolver {
|
||||
return guild.members.get(user.id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Channel. This can be:
|
||||
* * An instance of a Channel
|
||||
@@ -138,6 +121,26 @@ class ClientDataResolver {
|
||||
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)?\.(?:gg|com\/invite)\/([a-z0-9]{5})/i;
|
||||
const match = inviteRegex.exec(data);
|
||||
|
||||
if (match && match[1]) return match[1];
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a permission number. This can be:
|
||||
* * A string
|
||||
@@ -205,6 +208,23 @@ class ClientDataResolver {
|
||||
return String(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -49,6 +49,7 @@ class ClientManager {
|
||||
*/
|
||||
setupKeepAlive(time) {
|
||||
this.heartbeatInterval = this.client.setInterval(() => {
|
||||
this.client.emit('debug', 'Sending heartbeat');
|
||||
this.client.ws.send({
|
||||
op: Constants.OPCodes.HEARTBEAT,
|
||||
d: this.client.ws.sequence,
|
||||
|
||||
46
src/client/WebhookClient.js
Normal file
46
src/client/WebhookClient.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const Webhook = require('../structures/Webhook');
|
||||
const RESTManager = require('./rest/RESTManager');
|
||||
const ClientDataResolver = require('./ClientDataResolver');
|
||||
const mergeDefault = require('../util/MergeDefault');
|
||||
const Constants = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* The Webhook Client
|
||||
* @extends {Webhook}
|
||||
*/
|
||||
class WebhookClient extends Webhook {
|
||||
/**
|
||||
* @param {string} id The id of the webhook.
|
||||
* @param {string} token the token of the webhook.
|
||||
* @param {ClientOptions} [options] Options for the client
|
||||
* @example
|
||||
* // create a new webhook and send a message
|
||||
* let hook = new Discord.WebhookClient('1234', 'abcdef')
|
||||
* hook.sendMessage('This will send a message').catch(console.log)
|
||||
*/
|
||||
constructor(id, token, options) {
|
||||
super(null, id, token);
|
||||
|
||||
/**
|
||||
* The options the client was instantiated with
|
||||
* @type {ClientOptions}
|
||||
*/
|
||||
this.options = 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);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebhookClient;
|
||||
@@ -1,13 +1,15 @@
|
||||
const Action = require('./Action');
|
||||
const Constants = require('../../util/Constants');
|
||||
|
||||
class GuildEmojiUpdateAction extends Action {
|
||||
handle(data, guild) {
|
||||
const client = this.client;
|
||||
for (let emoji of data.emojis) {
|
||||
const already = guild.emojis.has(emoji.id);
|
||||
emoji = client.dataManager.newEmoji(emoji, guild);
|
||||
if (already) client.emit(Constants.Events.GUILD_EMOJI_UPDATE, guild, emoji);
|
||||
if (already) {
|
||||
client.dataManager.updateEmoji(guild.emojis.get(emoji.id), emoji);
|
||||
} else {
|
||||
emoji = client.dataManager.newEmoji(emoji, guild);
|
||||
}
|
||||
}
|
||||
for (let emoji of guild.emojis) {
|
||||
if (!data.emoijs.has(emoji.id)) client.dataManager.killEmoji(emoji);
|
||||
@@ -21,7 +23,7 @@ class GuildEmojiUpdateAction extends Action {
|
||||
/**
|
||||
* Emitted whenever an emoji is updated
|
||||
* @event Client#guildEmojiUpdate
|
||||
* @param {Guild} guild The guild that the emoji was updated in.
|
||||
* @param {Emoji} emoji The emoji that was updated.
|
||||
* @param {Emoji} oldEmoji The old emoji
|
||||
* @param {Emoji} newEmoji The new emoji
|
||||
*/
|
||||
module.exports = GuildEmojiUpdateAction;
|
||||
|
||||
@@ -7,13 +7,15 @@ const User = requireStructure('User');
|
||||
const GuildMember = requireStructure('GuildMember');
|
||||
const Role = requireStructure('Role');
|
||||
const Invite = requireStructure('Invite');
|
||||
const Webhook = requireStructure('Webhook');
|
||||
|
||||
class RESTMethods {
|
||||
constructor(restManager) {
|
||||
this.rest = restManager;
|
||||
}
|
||||
|
||||
loginToken(token) {
|
||||
loginToken(token = this.rest.client.token) {
|
||||
token = token.replace(/^Bot\s*/i, '');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.client.manager.connectToWebSocket(token, resolve, reject);
|
||||
});
|
||||
@@ -26,7 +28,7 @@ class RESTMethods {
|
||||
this.rest.client.password = password;
|
||||
this.rest.makeRequest('post', Constants.Endpoints.login, false, { email, password })
|
||||
.then(data => {
|
||||
this.rest.client.manager.connectToWebSocket(data.token, resolve, reject);
|
||||
resolve(this.loginToken(data.token));
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
@@ -47,21 +49,26 @@ class RESTMethods {
|
||||
});
|
||||
}
|
||||
|
||||
getBotGateway() {
|
||||
return this.rest.makeRequest('get', Constants.Endpoints.botGateway, true);
|
||||
}
|
||||
|
||||
sendMessage(channel, content, { tts, nonce, disableEveryone, split } = {}, file = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof content !== 'undefined') content = this.rest.client.resolver.resolveString(content);
|
||||
|
||||
if (disableEveryone || (typeof disableEveryone === 'undefined' && this.rest.client.options.disableEveryone)) {
|
||||
content = content.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere');
|
||||
}
|
||||
if (content) {
|
||||
if (disableEveryone || (typeof disableEveryone === 'undefined' && this.rest.client.options.disableEveryone)) {
|
||||
content = content.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere');
|
||||
}
|
||||
|
||||
if (split) content = splitMessage(content, typeof split === 'object' ? split : {});
|
||||
if (split) content = splitMessage(content, typeof split === 'object' ? split : {});
|
||||
}
|
||||
|
||||
if (channel instanceof User || channel instanceof GuildMember) {
|
||||
this.createDM(channel).then(chan => {
|
||||
this._sendMessageRequest(chan, content, file, tts, nonce, resolve, reject);
|
||||
})
|
||||
.catch(reject);
|
||||
}).catch(reject);
|
||||
} else {
|
||||
this._sendMessageRequest(channel, content, file, tts, nonce, resolve, reject);
|
||||
}
|
||||
@@ -71,22 +78,24 @@ class RESTMethods {
|
||||
_sendMessageRequest(channel, content, file, tts, nonce, resolve, reject) {
|
||||
if (content instanceof Array) {
|
||||
const datas = [];
|
||||
const promise = this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, {
|
||||
let promise = this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, {
|
||||
content: content[0], tts, nonce,
|
||||
}, file).catch(reject);
|
||||
|
||||
for (let i = 1; i <= content.length; i++) {
|
||||
if (i < content.length) {
|
||||
promise.then(data => {
|
||||
const i2 = i;
|
||||
promise = promise.then(data => {
|
||||
datas.push(data);
|
||||
return this.rest.makeRequest('post', Constants.Endpoints.channelMessages(channel.id), true, {
|
||||
content: content[i], tts, nonce,
|
||||
content: content[i2], tts, nonce,
|
||||
}, file);
|
||||
});
|
||||
}).catch(reject);
|
||||
} else {
|
||||
promise.then(data => {
|
||||
datas.push(data);
|
||||
resolve(this.rest.client.actions.MessageCreate.handle(datas).messages);
|
||||
});
|
||||
}).catch(reject);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -535,6 +544,138 @@ class RESTMethods {
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
getWebhook(id, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('get', Constants.Endpoints.webhook(id, token), require('util').isUndefined(token))
|
||||
.then(data => {
|
||||
resolve(new Webhook(this.rest.client, data));
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
getGuildWebhooks(guild) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('get', Constants.Endpoints.guildWebhooks(guild.id), true)
|
||||
.then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) {
|
||||
hooks.set(hook.id, new Webhook(this.rest.client, hook));
|
||||
}
|
||||
resolve(hooks);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
getChannelWebhooks(channel) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('get', Constants.Endpoints.channelWebhooks(channel.id), true)
|
||||
.then(data => {
|
||||
const hooks = new Collection();
|
||||
for (const hook of data) {
|
||||
hooks.set(hook.id, new Webhook(this.rest.client, hook));
|
||||
}
|
||||
resolve(hooks);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
createWebhook(channel, name, avatar) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('post', Constants.Endpoints.channelWebhooks(channel.id), true, {
|
||||
name,
|
||||
avatar,
|
||||
})
|
||||
.then(data => {
|
||||
resolve(new Webhook(this.rest.client, data));
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
editWebhook(webhook, name, avatar) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('patch', Constants.Endpoints.webhook(webhook.id, webhook.token), false, {
|
||||
name,
|
||||
avatar,
|
||||
}).then(data => {
|
||||
webhook.name = data.name;
|
||||
webhook.avatar = data.avatar;
|
||||
resolve(webhook);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
deleteWebhook(webhook) {
|
||||
return this.rest.makeRequest('delete', Constants.Endpoints.webhook(webhook.id, webhook.token), false);
|
||||
}
|
||||
|
||||
sendWebhookMessage(webhook, content, { avatarURL, tts, disableEveryone, embeds } = {}, file = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof content !== 'undefined') content = this.rest.client.resolver.resolveString(content);
|
||||
|
||||
if (disableEveryone || (typeof disableEveryone === 'undefined' && this.rest.client.options.disableEveryone)) {
|
||||
content = content.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere');
|
||||
}
|
||||
|
||||
this.rest.makeRequest('post', `${Constants.Endpoints.webhook(webhook.id, webhook.token)}?wait=true`, false, {
|
||||
content: content, username: webhook.name, avatar_url: avatarURL, tts: tts, file: file, embeds: embeds,
|
||||
})
|
||||
.then(data => {
|
||||
resolve(data);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
sendSlackWebhookMessage(webhook, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest(
|
||||
'post',
|
||||
`${Constants.Endpoints.webhook(webhook.id, webhook.token)}/slack?wait=true`,
|
||||
false,
|
||||
body
|
||||
).then(data => {
|
||||
resolve(data);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
addFriend(user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('post', Constants.Endpoints.relationships('@me'), true, {
|
||||
discriminator: user.discriminator,
|
||||
username: user.username,
|
||||
}).then(() => {
|
||||
resolve(user);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
removeFriend(user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true)
|
||||
.then(() => {
|
||||
resolve(user);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
blockUser(user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('put', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true, { type: 2 })
|
||||
.then(() => {
|
||||
resolve(user);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
unblockUser(user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true)
|
||||
.then(() => {
|
||||
resolve(user);
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTMethods;
|
||||
|
||||
@@ -59,6 +59,13 @@ class WebSocketManager extends EventEmitter {
|
||||
*/
|
||||
this.ws = null;
|
||||
|
||||
/**
|
||||
* An object with keys that are websocket event names that should be ignored
|
||||
* @type {Object}
|
||||
*/
|
||||
this.disabledEvents = {};
|
||||
for (const event in client.options.disabledEvents) this.disabledEvents[event] = true;
|
||||
|
||||
this.first = true;
|
||||
}
|
||||
|
||||
@@ -69,9 +76,7 @@ class WebSocketManager extends EventEmitter {
|
||||
_connect(gateway) {
|
||||
this.client.emit('debug', `Connecting to gateway ${gateway}`);
|
||||
this.normalReady = false;
|
||||
if (this.status !== Constants.Status.RECONNECTING) {
|
||||
this.status = Constants.Status.CONNECTING;
|
||||
}
|
||||
if (this.status !== Constants.Status.RECONNECTING) this.status = Constants.Status.CONNECTING;
|
||||
this.ws = new WebSocket(gateway);
|
||||
this.ws.onopen = () => this.eventOpen();
|
||||
this.ws.onclose = (d) => this.eventClose(d);
|
||||
@@ -216,7 +221,7 @@ class WebSocketManager extends EventEmitter {
|
||||
|
||||
this.client.emit('raw', packet);
|
||||
|
||||
if (packet.op === 10) this.client.manager.setupKeepAlive(packet.d.heartbeat_interval);
|
||||
if (packet.op === Constants.OPCodes.HELLO) this.client.manager.setupKeepAlive(packet.d.heartbeat_interval);
|
||||
return this.packetManager.handle(packet);
|
||||
}
|
||||
|
||||
@@ -258,9 +263,10 @@ class WebSocketManager extends EventEmitter {
|
||||
if (unavailableCount === 0) {
|
||||
this.status = Constants.Status.NEARLY;
|
||||
if (this.client.options.fetchAllMembers) {
|
||||
const promises = this.client.guilds.array().map(g => g.fetchMembers());
|
||||
const promises = this.client.guilds.map(g => g.fetchMembers());
|
||||
Promise.all(promises).then(() => this._emitReady()).catch(e => {
|
||||
this.client.emit(Constants.Event.WARN, `Error on pre-ready guild member fetching - ${e}`);
|
||||
this.client.emit(Constants.Events.WARN, 'Error in pre-ready guild member fetching');
|
||||
this.client.emit(Constants.Events.ERROR, e);
|
||||
this._emitReady();
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -42,6 +42,8 @@ class WebSocketPacketManager {
|
||||
this.register(Constants.WSEvents.MESSAGE_DELETE_BULK, 'MessageDeleteBulk');
|
||||
this.register(Constants.WSEvents.CHANNEL_PINS_UPDATE, 'ChannelPinsUpdate');
|
||||
this.register(Constants.WSEvents.GUILD_SYNC, 'GuildSync');
|
||||
this.register(Constants.WSEvents.RELATIONSHIP_ADD, 'RelationshipAdd');
|
||||
this.register(Constants.WSEvents.RELATIONSHIP_REMOVE, 'RelationshipRemove');
|
||||
}
|
||||
|
||||
get client() {
|
||||
@@ -77,6 +79,8 @@ class WebSocketPacketManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet.op === Constants.OPCodes.HEARTBEAT_ACK) this.ws.client.emit('debug', 'Heartbeat acknowledged');
|
||||
|
||||
if (this.ws.status === Constants.Status.RECONNECTING) {
|
||||
this.ws.reconnecting = false;
|
||||
this.ws.checkIfReady();
|
||||
@@ -84,6 +88,8 @@ class WebSocketPacketManager {
|
||||
|
||||
this.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);
|
||||
|
||||
@@ -10,12 +10,21 @@ class ReadyHandler extends AbstractHandler {
|
||||
|
||||
const clientUser = new ClientUser(client, data.user);
|
||||
client.user = clientUser;
|
||||
client.readyTime = new Date();
|
||||
client.readyAt = new Date();
|
||||
client.users.set(clientUser.id, clientUser);
|
||||
|
||||
for (const guild of data.guilds) client.dataManager.newGuild(guild);
|
||||
for (const privateDM of data.private_channels) client.dataManager.newChannel(privateDM);
|
||||
|
||||
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);
|
||||
|
||||
19
src/client/websocket/packets/handlers/RelationshipAdd.js
Normal file
19
src/client/websocket/packets/handlers/RelationshipAdd.js
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
src/client/websocket/packets/handlers/RelationshipRemove.js
Normal file
19
src/client/websocket/packets/handlers/RelationshipRemove.js
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;
|
||||
Reference in New Issue
Block a user