mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 16:43:31 +01:00
feat(BitField): add BitField base class (#3759)
* feat(BitField): add BitField base class * fix(Permissions): properly deprecate the getters/setters
This commit is contained in:
@@ -418,7 +418,7 @@ class Client extends EventEmitter {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
generateInvite(permissions) {
|
||||
permissions = typeof permissions === 'undefined' ? 0 : Permissions.resolve(permissions);
|
||||
permissions = Permissions.resolve(permissions);
|
||||
return this.fetchApplication().then(application =>
|
||||
`https://discordapp.com/oauth2/authorize?client_id=${application.id}&permissions=${permissions}&scope=bot`
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
WebhookClient: require('./client/WebhookClient'),
|
||||
|
||||
// Utilities
|
||||
BitField: require('./util/BitField'),
|
||||
Collection: require('./util/Collection'),
|
||||
Constants: require('./util/Constants'),
|
||||
DiscordAPIError: require('./client/rest/DiscordAPIError'),
|
||||
|
||||
@@ -192,6 +192,10 @@ class Game {
|
||||
return new Date(this.createdTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags that describe the activity
|
||||
* @type {ActivityFlags[]}
|
||||
*/
|
||||
get flags() {
|
||||
const flags = [];
|
||||
for (const [name, flag] of Object.entries(ActivityFlags)) {
|
||||
|
||||
160
src/util/BitField.js
Normal file
160
src/util/BitField.js
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Data structure that makes it easy to interact with a bitfield.
|
||||
*/
|
||||
class BitField {
|
||||
/**
|
||||
* @param {BitFieldResolvable} [bits=0] Bits(s) to read from
|
||||
*/
|
||||
constructor(bits) {
|
||||
/**
|
||||
* Bitfield of the packed bits
|
||||
* @type {number}
|
||||
*/
|
||||
this.bitfield = this.constructor.resolve(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a bit, or any of multiple bits.
|
||||
* @param {BitFieldResolvable} bit Bit(s) to check for
|
||||
* @returns {boolean}
|
||||
*/
|
||||
any(bit) {
|
||||
return (this.bitfield & this.constructor.resolve(bit)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this bitfield equals another
|
||||
* @param {BitFieldResolvable} bit Bit(s) to check for
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals(bit) {
|
||||
return this.bitfield === this.constructor.resolve(bit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a bit, or multiple bits.
|
||||
* @param {BitFieldResolvable} bit Bit(s) to check for
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(bit) {
|
||||
if (Array.isArray(bit)) return bit.every(p => this.has(p));
|
||||
bit = this.constructor.resolve(bit);
|
||||
return (this.bitfield & bit) === bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all given bits that are missing from the bitfield.
|
||||
* @param {BitFieldResolvable} bits Bits(s) to check for
|
||||
* @param {...*} hasParams Additional parameters for the has method, if any
|
||||
* @returns {string[]}
|
||||
*/
|
||||
missing(bits, ...hasParams) {
|
||||
if (!Array.isArray(bits)) bits = new this.constructor(bits).toArray(false);
|
||||
return bits.filter(p => !this.has(p, ...hasParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes these bits, making them immutable.
|
||||
* @returns {Readonly<BitField>} These bits
|
||||
*/
|
||||
freeze() {
|
||||
return Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds bits to these ones.
|
||||
* @param {...BitFieldResolvable} [bits] Bits to add
|
||||
* @returns {BitField} These bits or new BitField if the instance is frozen.
|
||||
*/
|
||||
add(...bits) {
|
||||
let total = 0;
|
||||
for (const bit of bits) {
|
||||
total |= this.constructor.resolve(bit);
|
||||
}
|
||||
if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
|
||||
this.bitfield |= total;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes bits from these.
|
||||
* @param {...BitFieldResolvable} [bits] Bits to remove
|
||||
* @returns {BitField} These bits or new BitField if the instance is frozen.
|
||||
*/
|
||||
remove(...bits) {
|
||||
let total = 0;
|
||||
for (const bit of bits) {
|
||||
total |= this.constructor.resolve(bit);
|
||||
}
|
||||
if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
|
||||
this.bitfield &= ~total;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object mapping field names to a {@link boolean} indicating whether the
|
||||
* bit is available.
|
||||
* @param {...*} hasParams Additional parameters for the has method, if any
|
||||
* @returns {Object}
|
||||
*/
|
||||
serialize(...hasParams) {
|
||||
const serialized = {};
|
||||
for (const flag of Object.keys(this.constructor.FLAGS)) {
|
||||
serialized[flag] = this.has(this.constructor.FLAGS[flag], ...hasParams);
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link Array} of bitfield names based on the bits available.
|
||||
* @param {...*} hasParams Additional parameters for the has method, if any
|
||||
* @returns {string[]}
|
||||
*/
|
||||
toArray(...hasParams) {
|
||||
return Object.keys(this.constructor.FLAGS).filter(bit => this.has(bit, ...hasParams));
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
yield* this.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a bitfield. This can be:
|
||||
* * A string (see {@link BitField.FLAGS})
|
||||
* * A bit number
|
||||
* * An instance of BitField
|
||||
* * An Array of BitFieldResolvable
|
||||
* @typedef {string|number|BitField|BitFieldResolvable[]} BitFieldResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves bitfields to their numeric form.
|
||||
* @param {BitFieldResolvable} [bit=0] - bit(s) to resolve
|
||||
* @returns {number}
|
||||
*/
|
||||
static resolve(bit = 0) {
|
||||
if (typeof bit === 'number' && bit >= 0) return bit;
|
||||
if (bit instanceof BitField) return bit.bitfield;
|
||||
if (Array.isArray(bit)) return bit.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
|
||||
if (typeof bit === 'string' && typeof this.FLAGS[bit] !== 'undefined') return this.FLAGS[bit];
|
||||
throw new RangeError('Invalid bitfield flag or number.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Numeric bitfield flags.
|
||||
* <info>Defined in extension classes</info>
|
||||
* @type {Object}
|
||||
* @abstract
|
||||
*/
|
||||
BitField.FLAGS = {};
|
||||
|
||||
module.exports = BitField;
|
||||
@@ -386,6 +386,17 @@ exports.ActivityTypes = [
|
||||
'CUSTOM_STATUS',
|
||||
];
|
||||
|
||||
/**
|
||||
* Numeric activity flags. All available properties:
|
||||
* * `INSTANCE`
|
||||
* * `JOIN`
|
||||
* * `SPECTATE`
|
||||
* * `JOIN_REQUEST`
|
||||
* * `SYNC`
|
||||
* * `PLAY`
|
||||
* @typedef {string} ActivityFlag
|
||||
* @see {@link https://discordapp.com/developers/docs/topics/gateway#activity-object-activity-flags}
|
||||
*/
|
||||
exports.ActivityFlags = {
|
||||
INSTANCE: 1 << 0,
|
||||
JOIN: 1 << 1,
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
const Constants = require('../util/Constants');
|
||||
const BitField = require('./BitField');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
* Data structure that makes it easy to interact with a permission bitfield. All {@link GuildMember}s have a set of
|
||||
* permissions in their guild, and each channel in the guild may also have {@link PermissionOverwrites} for the member
|
||||
* that override their default permissions.
|
||||
* @extends {BitField}
|
||||
*/
|
||||
class Permissions {
|
||||
class Permissions extends BitField {
|
||||
/**
|
||||
* @param {GuildMember} [member] Member the permissions are for **(deprecated)**
|
||||
* @param {number|PermissionResolvable} permissions Permissions or bitfield to read from
|
||||
*/
|
||||
constructor(member, permissions) {
|
||||
permissions = typeof member === 'object' && !(member instanceof Array) ? permissions : member;
|
||||
super(typeof member === 'object' && !(member instanceof Array) ? permissions : member);
|
||||
|
||||
/**
|
||||
Object.defineProperty(this, '_member', {
|
||||
writable: true,
|
||||
value: typeof member === 'object' && !(member instanceof Array) ? member : null,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Member the permissions are for
|
||||
* @type {GuildMember}
|
||||
* @deprecated
|
||||
*/
|
||||
this._member = typeof member === 'object' ? member : null;
|
||||
|
||||
/**
|
||||
* Bitfield of the packed permissions
|
||||
* @type {number}
|
||||
*/
|
||||
this.bitfield = typeof permissions === 'number' ? permissions : this.constructor.resolve(permissions);
|
||||
}
|
||||
|
||||
get member() {
|
||||
return this._member;
|
||||
}
|
||||
@@ -51,6 +49,16 @@ class Permissions {
|
||||
this.bitfield = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or any of multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
any(permission, checkAdmin = true) {
|
||||
return (checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) || super.any(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or multiple permissions.
|
||||
* @param {PermissionResolvable} permission Permission(s) to check for
|
||||
@@ -58,76 +66,7 @@ class Permissions {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
has(permission, checkAdmin = true) {
|
||||
if (permission instanceof Array) return permission.every(p => this.has(p, checkAdmin));
|
||||
permission = this.constructor.resolve(permission);
|
||||
if (checkAdmin && (this.bitfield & this.constructor.FLAGS.ADMINISTRATOR) > 0) return true;
|
||||
return (this.bitfield & permission) === permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bitfield has a permission, or any of multiple permissions.
|
||||
* @param {PermissionResolvable} permissions Permission(s) to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {boolean}
|
||||
*/
|
||||
any(permissions, checkAdmin = true) {
|
||||
return (checkAdmin && this.has(this.constructor.FLAGS.ADMINISTRATOR)) ||
|
||||
(this.bitfield & this.constructor.resolve(permissions)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all given permissions that are missing from the bitfield.
|
||||
* @param {PermissionResolvable} permissions Permissions to check for
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {PermissionResolvable}
|
||||
*/
|
||||
missing(permissions, checkAdmin = true) {
|
||||
if (!(permissions instanceof Array)) permissions = [permissions];
|
||||
return permissions.filter(p => !this.has(p, checkAdmin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds permissions to this one, creating a new instance to represent the new bitfield.
|
||||
* @param {...PermissionResolvable} permissions Permissions to add
|
||||
* @returns {Permissions}
|
||||
*/
|
||||
add(...permissions) {
|
||||
let total = 0;
|
||||
for (let p = permissions.length - 1; p >= 0; p--) {
|
||||
const perm = this.constructor.resolve(permissions[p]);
|
||||
total |= perm;
|
||||
}
|
||||
if (Object.isFrozen(this)) return new this.constructor(this.bitfield | total);
|
||||
this.bitfield |= total;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes permissions to this one, creating a new instance to represent the new bitfield.
|
||||
* @param {...PermissionResolvable} permissions Permissions to remove
|
||||
* @returns {Permissions}
|
||||
*/
|
||||
remove(...permissions) {
|
||||
let total = 0;
|
||||
for (let p = permissions.length - 1; p >= 0; p--) {
|
||||
const perm = this.constructor.resolve(permissions[p]);
|
||||
total |= perm;
|
||||
}
|
||||
if (Object.isFrozen(this)) return new this.constructor(this.bitfield & ~total);
|
||||
this.bitfield &= ~total;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object mapping permission name (like `VIEW_CHANNEL`) to a {@link boolean} indicating whether the
|
||||
* permission is available.
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {Object}
|
||||
*/
|
||||
serialize(checkAdmin = true) {
|
||||
const serialized = {};
|
||||
for (const perm of Object.keys(this.constructor.FLAGS)) serialized[perm] = this.has(perm, checkAdmin);
|
||||
return serialized;
|
||||
return (checkAdmin && super.has(this.constructor.FLAGS.ADMINISTRATOR)) || super.has(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,46 +105,12 @@ class Permissions {
|
||||
return this.missing(permissions, !explicit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link Array} of permission names (such as `VIEW_CHANNEL`) based on the permissions available.
|
||||
* @param {boolean} [checkAdmin=true] Whether to allow the administrator permission to override
|
||||
* @returns {string[]}
|
||||
*/
|
||||
toArray(checkAdmin = true) {
|
||||
return Object.keys(this.constructor.FLAGS).filter(perm => this.has(perm, checkAdmin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes these permissions, making them immutable.
|
||||
* @returns {Permissions} These permissions
|
||||
*/
|
||||
freeze() {
|
||||
return Object.freeze(this);
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that can be resolved to give a permission number. This can be:
|
||||
* * A string (see {@link Permissions.FLAGS})
|
||||
* * A permission number
|
||||
* @typedef {string|number|Permissions|PermissionResolvable[]} PermissionResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves permissions to their numeric form.
|
||||
* @param {PermissionResolvable} permission - Permission(s) to resolve
|
||||
* @returns {number}
|
||||
*/
|
||||
static resolve(permission) {
|
||||
if (permission instanceof Array) return permission.map(p => this.resolve(p)).reduce((prev, p) => prev | p, 0);
|
||||
if (permission instanceof Permissions) return permission.bitfield;
|
||||
if (typeof permission === 'string') permission = this.FLAGS[permission];
|
||||
if (typeof permission !== 'number' || permission < 0) throw new RangeError(Constants.Errors.NOT_A_PERMISSION);
|
||||
return permission;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,10 +215,20 @@ Permissions.prototype.hasPermissions = util.deprecate(Permissions.prototype.hasP
|
||||
'EvaluatedPermissions#hasPermissions is deprecated, use Permissions#has instead');
|
||||
Permissions.prototype.missingPermissions = util.deprecate(Permissions.prototype.missingPermissions,
|
||||
'EvaluatedPermissions#missingPermissions is deprecated, use Permissions#missing instead');
|
||||
Object.defineProperty(Permissions.prototype, 'raw', {
|
||||
get: util
|
||||
.deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'raw').get,
|
||||
'EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead'),
|
||||
set: util.deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'raw').set,
|
||||
'EvaluatedPermissions#raw is deprecated use Permissions#bitfield instead'),
|
||||
});
|
||||
Object.defineProperty(Permissions.prototype, 'member', {
|
||||
get: util
|
||||
.deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'member').get,
|
||||
'EvaluatedPermissions#member is deprecated'),
|
||||
set: util
|
||||
.deprecate(Object.getOwnPropertyDescriptor(Permissions.prototype, 'member').set,
|
||||
'EvaluatedPermissions#member is deprecated'),
|
||||
});
|
||||
|
||||
module.exports = Permissions;
|
||||
|
||||
Reference in New Issue
Block a user