From 6fa4fc532cf9525838b55287a5565be4a4ec07d2 Mon Sep 17 00:00:00 2001 From: SpaceEEC Date: Fri, 17 Nov 2017 08:49:57 +0100 Subject: [PATCH 1/3] fix(Shard): extend EventEmitter to be able to emit events (#2112) --- src/sharding/Shard.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sharding/Shard.js b/src/sharding/Shard.js index 76da23527..7e2a51ac7 100644 --- a/src/sharding/Shard.js +++ b/src/sharding/Shard.js @@ -1,4 +1,5 @@ const childProcess = require('child_process'); +const EventEmitter = require('events'); const path = require('path'); const Util = require('../util/Util'); const { Error } = require('../errors'); @@ -6,13 +7,14 @@ const { Error } = require('../errors'); /** * Represents a Shard spawned by the ShardingManager. */ -class Shard { +class Shard extends EventEmitter { /** * @param {ShardingManager} manager Manager that is spawning this shard * @param {number} id ID of this shard * @param {string[]} [args=[]] Command line arguments to pass to the script */ constructor(manager, id, args = []) { + super(); /** * Manager that created the shard * @type {ShardingManager} From 0cd4a92fb8d7e041bbf600c5b7fdb3af55460680 Mon Sep 17 00:00:00 2001 From: Frangu Vlad Date: Fri, 17 Nov 2017 15:20:57 +0200 Subject: [PATCH 2/3] docs: Fixed some missing docstrings or incorrect return types (#2093) * Fix some missing doc strings Mainly just readonly tags * Return an error when guild#allowDMs is ran from a bot account, and fix some return types * WebhookClient implements Webhook, doesn't extend it * Fix Client#rateLimit docs not showing what it returns Cause I wanted to handle this event only to see no return props :thinking: * Actually make Client#rateLimit show the right info Its an object with all the info --- src/client/BaseClient.js | 1 + src/client/Client.js | 1 + src/client/WebhookClient.js | 2 +- src/rest/handlers/RequestHandler.js | 13 +++++++------ src/structures/ClientUser.js | 2 +- src/structures/Guild.js | 5 +++-- src/structures/GuildMember.js | 6 ++++++ src/util/Constants.js | 2 +- 8 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/client/BaseClient.js b/src/client/BaseClient.js index 365e9dcb7..f2c91ccdf 100644 --- a/src/client/BaseClient.js +++ b/src/client/BaseClient.js @@ -42,6 +42,7 @@ class BaseClient extends EventEmitter { /** * API shortcut * @type {Object} + * @readonly * @private */ get api() { diff --git a/src/client/Client.js b/src/client/Client.js index b26b73e2e..4b335e88a 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -163,6 +163,7 @@ class Client extends BaseClient { /** * Timestamp of the latest ping's start time * @type {number} + * @readonly * @private */ get _pingTimestamp() { diff --git a/src/client/WebhookClient.js b/src/client/WebhookClient.js index bc413cef6..c4c297879 100644 --- a/src/client/WebhookClient.js +++ b/src/client/WebhookClient.js @@ -3,7 +3,7 @@ const BaseClient = require('./BaseClient'); /** * The webhook client. - * @extends {Webhook} + * @implements {Webhook} * @extends {BaseClient} */ class WebhookClient extends BaseClient { diff --git a/src/rest/handlers/RequestHandler.js b/src/rest/handlers/RequestHandler.js index 64e9ea72b..c4226a45c 100644 --- a/src/rest/handlers/RequestHandler.js +++ b/src/rest/handlers/RequestHandler.js @@ -39,12 +39,13 @@ class RequestHandler { /** * Emitted when the client hits a rate limit while making a request * @event Client#rateLimit - * @prop {number} timeout Timeout in ms - * @prop {number} limit Number of requests that can be made to this endpoint - * @prop {number} timeDifference Delta-T in ms between your system and Discord servers - * @prop {string} method HTTP method used for request that triggered this event - * @prop {string} path Path used for request that triggered this event - * @prop {string} route Route used for request that triggered this event + * @param {Object} rateLimitInfo Object containing the rate limit info + * @param {number} rateLimitInfo.timeout Timeout in ms + * @param {number} rateLimitInfo.limit Number of requests that can be made to this endpoint + * @param {number} rateLimitInfo.timeDifference Delta-T in ms between your system and Discord servers + * @param {string} rateLimitInfo.method HTTP method used for request that triggered this event + * @param {string} rateLimitInfo.path Path used for request that triggered this event + * @param {string} rateLimitInfo.route Route used for request that triggered this event */ this.client.emit(RATE_LIMIT, { timeout, diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index ce71d9bcf..ef6433924 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -248,7 +248,7 @@ class ClientUser extends User { /** * Fetches messages that mentioned the client's user. * This is only available when using a user account. - * @param {Object} [options] Options for the fetch + * @param {Object} [options={}] Options for the fetch * @param {number} [options.limit=25] Maximum number of mentions to retrieve * @param {boolean} [options.roles=true] Whether to include role mentions * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions diff --git a/src/structures/Guild.js b/src/structures/Guild.js index b84117e91..8c796d337 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -321,7 +321,7 @@ class Guild extends Base { /** * System channel for this guild - * @type {?GuildChannel} + * @type {?TextChannel} * @readonly */ get systemChannel() { @@ -806,6 +806,7 @@ class Guild extends Base { * @returns {Promise} */ allowDMs(allow) { + if (this.client.user.bot) return Promise.reject(new Error('FEATURE_USER_ONLY')); const settings = this.client.user.settings; if (allow) return settings.removeRestrictedGuild(this); else return settings.addRestrictedGuild(this); @@ -818,7 +819,7 @@ class Guild extends Base { * string, the ban reason. Supplying an object allows you to do both. * @param {number} [options.days=0] Number of days of messages to delete * @param {string} [options.reason] Reason for banning - * @returns {Promise} Result object will be resolved as specifically as possible. + * @returns {Promise} Result object will be resolved as specifically as possible. * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot * be resolved, the user ID will be the result. * @example diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 7ab2597fd..05266e1b8 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -77,36 +77,42 @@ class GuildMember extends Base { /** * Whether this member is deafened server-wide * @type {boolean} + * @readonly */ get serverDeaf() { return this.voiceState.deaf; } /** * Whether this member is muted server-wide * @type {boolean} + * @readonly */ get serverMute() { return this.voiceState.mute; } /** * Whether this member is self-muted * @type {boolean} + * @readonly */ get selfMute() { return this.voiceState.self_mute; } /** * Whether this member is self-deafened * @type {boolean} + * @readonly */ get selfDeaf() { return this.voiceState.self_deaf; } /** * The voice session ID of this member (if any) * @type {?Snowflake} + * @readonly */ get voiceSessionID() { return this.voiceState.session_id; } /** * The voice channel ID of this member, (if any) * @type {?Snowflake} + * @readonly */ get voiceChannelID() { return this.voiceState.channel_id; } diff --git a/src/util/Constants.js b/src/util/Constants.js index 7a0b262d3..efeeb29fa 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -52,7 +52,7 @@ exports.DefaultOptions = { * WebSocket options (these are left as snake_case to match the API) * @typedef {Object} WebsocketOptions * @property {number} [large_threshold=250] Number of members in a guild to be considered large - * @property {boolean} [compress=true] Whether to compress data sent on the connection + * @property {boolean} [compress=false] Whether to compress data sent on the connection * (defaults to `false` for browsers) */ ws: { From 8237bc054ce2d705ea5432dfbcf07c4fbd452d1a Mon Sep 17 00:00:00 2001 From: Drahcirius Date: Fri, 17 Nov 2017 08:37:07 -0500 Subject: [PATCH 3/3] So long, long (#1994) * refactor: remove long dep * fix linter issue * remove file extensions * optimize methods --- package.json | 1 - src/structures/shared/Search.js | 10 +++---- src/util/Snowflake.js | 13 ++++---- src/util/Util.js | 53 +++++++++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 50fb6bb59..08e2b200d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "homepage": "https://github.com/hydrabolt/discord.js#readme", "runkitExampleFilename": "./docs/examples/ping.js", "dependencies": { - "long": "^3.0.0", "pako": "^1.0.0", "prism-media": "^0.0.2", "snekfetch": "^3.0.0", diff --git a/src/structures/shared/Search.js b/src/structures/shared/Search.js index a852a5086..3adca7fcc 100644 --- a/src/structures/shared/Search.js +++ b/src/structures/shared/Search.js @@ -1,4 +1,4 @@ -const long = require('long'); +const Util = require('../../util/Util'); const { TypeError } = require('../../errors'); /** @@ -40,17 +40,17 @@ module.exports = function 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(); + options.maxID = Util.binaryToID((options.before.getTime() - 14200704e5).toString(2) + '0'.repeat(22)); } 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(); + options.minID = Util.binaryToID((options.after.getTime() - 14200704e5).toString(2) + '0'.repeat(22)); } 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 + 864e5).shiftLeft(22).toString(); + options.minID = Util.binaryToID(t.toString(2) + '0'.repeat(22)); + options.maxID = Util.binaryToID((t + 864e5).toString(2) + '0'.repeat(22)); } if (options.channel) options.channel = target.client.channels.resolveID(options.channel); if (options.author) options.author = target.client.users.resolveID(options.author); diff --git a/src/util/Snowflake.js b/src/util/Snowflake.js index f16839108..27f9f7440 100644 --- a/src/util/Snowflake.js +++ b/src/util/Snowflake.js @@ -1,4 +1,4 @@ -const Long = require('long'); +const Util = require('../util/Util'); // Discord epoch (2015-01-01T00:00:00.000Z) const EPOCH = 1420070400000; @@ -31,8 +31,9 @@ class SnowflakeUtil { */ static generate() { if (INCREMENT >= 4095) INCREMENT = 0; - const BINARY = `${pad((Date.now() - EPOCH).toString(2), 42)}0000100000${pad((INCREMENT++).toString(2), 12)}`; - return Long.fromString(BINARY, 2).toString(); + // eslint-disable-next-line max-len + const BINARY = `${(Date.now() - EPOCH).toString(2).padStart(42, '0')}0000100000${(INCREMENT++).toString(2).padStart(12, '0')}`; + return Util.binaryToID(BINARY); } /** @@ -52,7 +53,7 @@ class SnowflakeUtil { * @returns {DeconstructedSnowflake} Deconstructed snowflake */ static deconstruct(snowflake) { - const BINARY = pad(Long.fromString(snowflake).toString(2), 64); + const BINARY = Util.idToBinary(snowflake).toString(2).padStart(64, '0'); const res = { timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH, workerID: parseInt(BINARY.substring(42, 47), 2), @@ -68,8 +69,4 @@ class SnowflakeUtil { } } -function pad(v, n, c = '0') { - return String(v).length >= n ? String(v) : (String(c).repeat(n) + v).slice(-n); -} - module.exports = SnowflakeUtil; diff --git a/src/util/Util.js b/src/util/Util.js index ecc1b93e1..46ce22367 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -1,4 +1,3 @@ -const Long = require('long'); const snekfetch = require('snekfetch'); const { Colors, DefaultOptions, Endpoints } = require('./Constants'); const { Error: DiscordError, RangeError, TypeError } = require('../errors'); @@ -299,7 +298,9 @@ class Util { */ static discordSort(collection) { return collection - .sort((a, b) => a.rawPosition - b.rawPosition || Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber()); + .sort((a, b) => a.rawPosition - b.rawPosition || + parseInt(a.id.slice(0, -10)) - parseInt(b.id.slice(0, -10)) || + parseInt(a.id.slice(10)) - parseInt(b.id.slice(10))); } static setPosition(item, position, relative, sorted, route, reason) { @@ -316,6 +317,54 @@ class Util { } return f; } + + /** + * Transform a snowflake from a decimal string to a bit string + * @param {string} num Snowflake to be transformed + * @returns {string} + * @private + */ + static idToBinary(num) { + let bin = ''; + let high = parseInt(num.slice(0, -10)) || 0; + let low = parseInt(num.slice(-10)); + while (low > 0 || high > 0) { + bin = String(low & 1) + bin; + low = Math.floor(low / 2); + if (high > 0) { + low += 5000000000 * (high % 2); + high = Math.floor(high / 2); + } + } + return bin; + } + + + /** + * Transform a snowflake from a bit string to a decimal string + * @param {string} num Bit string to be transformed + * @returns {string} + * @private + */ + static binaryToID(num) { + let dec = ''; + + while (num.length > 50) { + const high = parseInt(num.slice(0, -32), 2); + const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); + + dec = (low % 10).toString() + dec; + num = Math.floor(high / 10).toString(2) + Math.floor(low / 10).toString(2).padStart(32, '0'); + } + + num = parseInt(num, 2); + while (num > 0) { + dec = (num % 10).toString() + dec; + num = Math.floor(num / 10); + } + + return dec; + } } module.exports = Util;