This commit is contained in:
Schuyler Cebulskie
2017-11-18 17:12:31 -05:00
13 changed files with 85 additions and 28 deletions

View File

@@ -32,7 +32,6 @@
"homepage": "https://github.com/hydrabolt/discord.js#readme", "homepage": "https://github.com/hydrabolt/discord.js#readme",
"runkitExampleFilename": "./docs/examples/ping.js", "runkitExampleFilename": "./docs/examples/ping.js",
"dependencies": { "dependencies": {
"long": "^3.0.0",
"pako": "^1.0.0", "pako": "^1.0.0",
"prism-media": "^0.0.2", "prism-media": "^0.0.2",
"snekfetch": "^3.0.0", "snekfetch": "^3.0.0",

View File

@@ -42,6 +42,7 @@ class BaseClient extends EventEmitter {
/** /**
* API shortcut * API shortcut
* @type {Object} * @type {Object}
* @readonly
* @private * @private
*/ */
get api() { get api() {

View File

@@ -163,6 +163,7 @@ class Client extends BaseClient {
/** /**
* Timestamp of the latest ping's start time * Timestamp of the latest ping's start time
* @type {number} * @type {number}
* @readonly
* @private * @private
*/ */
get _pingTimestamp() { get _pingTimestamp() {

View File

@@ -3,7 +3,7 @@ const BaseClient = require('./BaseClient');
/** /**
* The webhook client. * The webhook client.
* @extends {Webhook} * @implements {Webhook}
* @extends {BaseClient} * @extends {BaseClient}
*/ */
class WebhookClient extends BaseClient { class WebhookClient extends BaseClient {

View File

@@ -39,12 +39,13 @@ class RequestHandler {
/** /**
* Emitted when the client hits a rate limit while making a request * Emitted when the client hits a rate limit while making a request
* @event Client#rateLimit * @event Client#rateLimit
* @prop {number} timeout Timeout in ms * @param {Object} rateLimitInfo Object containing the rate limit info
* @prop {number} limit Number of requests that can be made to this endpoint * @param {number} rateLimitInfo.timeout Timeout in ms
* @prop {number} timeDifference Delta-T in ms between your system and Discord servers * @param {number} rateLimitInfo.limit Number of requests that can be made to this endpoint
* @prop {string} method HTTP method used for request that triggered this event * @param {number} rateLimitInfo.timeDifference Delta-T in ms between your system and Discord servers
* @prop {string} path Path used for request that triggered this event * @param {string} rateLimitInfo.method HTTP method used for request that triggered this event
* @prop {string} route Route 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, { this.client.emit(RATE_LIMIT, {
timeout, timeout,

View File

@@ -1,4 +1,5 @@
const childProcess = require('child_process'); const childProcess = require('child_process');
const EventEmitter = require('events');
const path = require('path'); const path = require('path');
const Util = require('../util/Util'); const Util = require('../util/Util');
const { Error } = require('../errors'); const { Error } = require('../errors');
@@ -6,13 +7,14 @@ const { Error } = require('../errors');
/** /**
* Represents a Shard spawned by the ShardingManager. * Represents a Shard spawned by the ShardingManager.
*/ */
class Shard { class Shard extends EventEmitter {
/** /**
* @param {ShardingManager} manager Manager that is spawning this shard * @param {ShardingManager} manager Manager that is spawning this shard
* @param {number} id ID of this shard * @param {number} id ID of this shard
* @param {string[]} [args=[]] Command line arguments to pass to the script * @param {string[]} [args=[]] Command line arguments to pass to the script
*/ */
constructor(manager, id, args = []) { constructor(manager, id, args = []) {
super();
/** /**
* Manager that created the shard * Manager that created the shard
* @type {ShardingManager} * @type {ShardingManager}

View File

@@ -248,7 +248,7 @@ class ClientUser extends User {
/** /**
* Fetches messages that mentioned the client's user. * Fetches messages that mentioned the client's user.
* <warn>This is only available when using a user account.</warn> * <warn>This is only available when using a user account.</warn>
* @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 {number} [options.limit=25] Maximum number of mentions to retrieve
* @param {boolean} [options.roles=true] Whether to include role mentions * @param {boolean} [options.roles=true] Whether to include role mentions
* @param {boolean} [options.everyone=true] Whether to include everyone/here mentions * @param {boolean} [options.everyone=true] Whether to include everyone/here mentions

View File

@@ -321,7 +321,7 @@ class Guild extends Base {
/** /**
* System channel for this guild * System channel for this guild
* @type {?GuildChannel} * @type {?TextChannel}
* @readonly * @readonly
*/ */
get systemChannel() { get systemChannel() {
@@ -806,6 +806,7 @@ class Guild extends Base {
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
*/ */
allowDMs(allow) { allowDMs(allow) {
if (this.client.user.bot) return Promise.reject(new Error('FEATURE_USER_ONLY'));
const settings = this.client.user.settings; const settings = this.client.user.settings;
if (allow) return settings.removeRestrictedGuild(this); if (allow) return settings.removeRestrictedGuild(this);
else return settings.addRestrictedGuild(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. * 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 {number} [options.days=0] Number of days of messages to delete
* @param {string} [options.reason] Reason for banning * @param {string} [options.reason] Reason for banning
* @returns {Promise<GuildMember|User|string>} Result object will be resolved as specifically as possible. * @returns {Promise<GuildMember|User|Snowflake>} 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 * 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. * be resolved, the user ID will be the result.
* @example * @example

View File

@@ -77,36 +77,42 @@ class GuildMember extends Base {
/** /**
* Whether this member is deafened server-wide * Whether this member is deafened server-wide
* @type {boolean} * @type {boolean}
* @readonly
*/ */
get serverDeaf() { return this.voiceState.deaf; } get serverDeaf() { return this.voiceState.deaf; }
/** /**
* Whether this member is muted server-wide * Whether this member is muted server-wide
* @type {boolean} * @type {boolean}
* @readonly
*/ */
get serverMute() { return this.voiceState.mute; } get serverMute() { return this.voiceState.mute; }
/** /**
* Whether this member is self-muted * Whether this member is self-muted
* @type {boolean} * @type {boolean}
* @readonly
*/ */
get selfMute() { return this.voiceState.self_mute; } get selfMute() { return this.voiceState.self_mute; }
/** /**
* Whether this member is self-deafened * Whether this member is self-deafened
* @type {boolean} * @type {boolean}
* @readonly
*/ */
get selfDeaf() { return this.voiceState.self_deaf; } get selfDeaf() { return this.voiceState.self_deaf; }
/** /**
* The voice session ID of this member (if any) * The voice session ID of this member (if any)
* @type {?Snowflake} * @type {?Snowflake}
* @readonly
*/ */
get voiceSessionID() { return this.voiceState.session_id; } get voiceSessionID() { return this.voiceState.session_id; }
/** /**
* The voice channel ID of this member, (if any) * The voice channel ID of this member, (if any)
* @type {?Snowflake} * @type {?Snowflake}
* @readonly
*/ */
get voiceChannelID() { return this.voiceState.channel_id; } get voiceChannelID() { return this.voiceState.channel_id; }

View File

@@ -1,4 +1,4 @@
const long = require('long'); const Util = require('../../util/Util');
const { TypeError } = require('../../errors'); const { TypeError } = require('../../errors');
/** /**
@@ -40,17 +40,17 @@ module.exports = function search(target, options) {
if (typeof options === 'string') options = { content: options }; if (typeof options === 'string') options = { content: options };
if (options.before) { if (options.before) {
if (!(options.before instanceof Date)) options.before = new Date(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) {
if (!(options.after instanceof Date)) options.after = new Date(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) {
if (!(options.during instanceof Date)) options.during = new Date(options.during); if (!(options.during instanceof Date)) options.during = new Date(options.during);
const t = options.during.getTime() - 14200704e5; const t = options.during.getTime() - 14200704e5;
options.minID = long.fromNumber(t).shiftLeft(22).toString(); options.minID = Util.binaryToID(t.toString(2) + '0'.repeat(22));
options.maxID = long.fromNumber(t + 864e5).shiftLeft(22).toString(); options.maxID = Util.binaryToID((t + 864e5).toString(2) + '0'.repeat(22));
} }
if (options.channel) options.channel = target.client.channels.resolveID(options.channel); if (options.channel) options.channel = target.client.channels.resolveID(options.channel);
if (options.author) options.author = target.client.users.resolveID(options.author); if (options.author) options.author = target.client.users.resolveID(options.author);

View File

@@ -52,7 +52,7 @@ exports.DefaultOptions = {
* WebSocket options (these are left as snake_case to match the API) * WebSocket options (these are left as snake_case to match the API)
* @typedef {Object} WebsocketOptions * @typedef {Object} WebsocketOptions
* @property {number} [large_threshold=250] Number of members in a guild to be considered large * @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) * (defaults to `false` for browsers)
*/ */
ws: { ws: {

View File

@@ -1,4 +1,4 @@
const Long = require('long'); const Util = require('../util/Util');
// Discord epoch (2015-01-01T00:00:00.000Z) // Discord epoch (2015-01-01T00:00:00.000Z)
const EPOCH = 1420070400000; const EPOCH = 1420070400000;
@@ -31,8 +31,9 @@ class SnowflakeUtil {
*/ */
static generate() { static generate() {
if (INCREMENT >= 4095) INCREMENT = 0; if (INCREMENT >= 4095) INCREMENT = 0;
const BINARY = `${pad((Date.now() - EPOCH).toString(2), 42)}0000100000${pad((INCREMENT++).toString(2), 12)}`; // eslint-disable-next-line max-len
return Long.fromString(BINARY, 2).toString(); 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 * @returns {DeconstructedSnowflake} Deconstructed snowflake
*/ */
static deconstruct(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 = { const res = {
timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH, timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH,
workerID: parseInt(BINARY.substring(42, 47), 2), 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; module.exports = SnowflakeUtil;

View File

@@ -1,4 +1,3 @@
const Long = require('long');
const snekfetch = require('snekfetch'); const snekfetch = require('snekfetch');
const { Colors, DefaultOptions, Endpoints } = require('./Constants'); const { Colors, DefaultOptions, Endpoints } = require('./Constants');
const { Error: DiscordError, RangeError, TypeError } = require('../errors'); const { Error: DiscordError, RangeError, TypeError } = require('../errors');
@@ -299,7 +298,9 @@ class Util {
*/ */
static discordSort(collection) { static discordSort(collection) {
return 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) { static setPosition(item, position, relative, sorted, route, reason) {
@@ -316,6 +317,54 @@ class Util {
} }
return f; 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; module.exports = Util;