mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13: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;
|
||||
|
||||
41
typings/index.d.ts
vendored
41
typings/index.d.ts
vendored
@@ -41,6 +41,25 @@ declare module 'discord.js' {
|
||||
public options: object;
|
||||
}
|
||||
|
||||
export class BitField<S extends string> {
|
||||
constructor(bits?: BitFieldResolvable<S>);
|
||||
public bitfield: number;
|
||||
public add(...bits: BitFieldResolvable<S>[]): BitField<S>;
|
||||
public any(bit: BitFieldResolvable<S>): boolean;
|
||||
public equals(bit: BitFieldResolvable<S>): boolean;
|
||||
public freeze(): Readonly<BitField<S>>;
|
||||
public has(bit: BitFieldResolvable<S>): boolean;
|
||||
public missing(bits: BitFieldResolvable<S>, ...hasParam: readonly unknown[]): S[];
|
||||
public remove(...bits: BitFieldResolvable<S>[]): BitField<S>;
|
||||
public serialize(...hasParam: readonly unknown[]): Record<S, boolean>;
|
||||
public toArray(...hasParam: readonly unknown[]): S[];
|
||||
public toJSON(): number;
|
||||
public valueOf(): number;
|
||||
public [Symbol.iterator](): IterableIterator<S>;
|
||||
public static FLAGS: object;
|
||||
public static resolve(bit?: BitFieldResolvable<any>): number;
|
||||
}
|
||||
|
||||
export class CategoryChannel extends GuildChannel {
|
||||
public readonly children: Collection<Snowflake, GuildChannel>;
|
||||
}
|
||||
@@ -436,6 +455,9 @@ declare module 'discord.js' {
|
||||
|
||||
export class Game {
|
||||
constructor(data: object, presence: Presence);
|
||||
private _flags: string[];
|
||||
private syncID: string;
|
||||
|
||||
public applicationID: string;
|
||||
public assets: RichPresenceAssets;
|
||||
public details: string;
|
||||
@@ -456,8 +478,6 @@ declare module 'discord.js' {
|
||||
public url: string;
|
||||
public equals(game: Game): boolean;
|
||||
public toString(): string;
|
||||
private _flags: string[];
|
||||
private syncID: string;
|
||||
}
|
||||
|
||||
export class GroupDMChannel extends TextBasedChannel(Channel) {
|
||||
@@ -1006,30 +1026,27 @@ declare module 'discord.js' {
|
||||
public delete(reason?: string): Promise<PermissionOverwrites>;
|
||||
}
|
||||
|
||||
export class Permissions {
|
||||
export class Permissions extends BitField<PermissionString> {
|
||||
constructor(permissions: PermissionResolvable);
|
||||
constructor(member: GuildMember, permissions: PermissionResolvable);
|
||||
private readonly raw: number;
|
||||
|
||||
public bitfield: number;
|
||||
public member: GuildMember;
|
||||
public add(...permissions: PermissionResolvable[]): this;
|
||||
public readonly raw: number;
|
||||
public any(permissions: PermissionResolvable, checkAdmin?: boolean): boolean;
|
||||
public freeze(): this;
|
||||
public has(permission: PermissionResolvable, checkAdmin?: boolean): boolean;
|
||||
public hasPermission(permission: PermissionResolvable, explicit?: boolean): boolean;
|
||||
public hasPermissions(permissions: PermissionResolvable, explicit?: boolean): boolean;
|
||||
public missing(permissions: PermissionResolvable, checkAdmin?: boolean): PermissionResolvable;
|
||||
public missing(permissions: PermissionResolvable, checkAdmin?: boolean): PermissionString[];
|
||||
public missingPermissions(permissions: PermissionResolvable, checkAdmin?: boolean): PermissionResolvable;
|
||||
public remove(...permissions: PermissionResolvable[]): this;
|
||||
public serialize(checkAdmin?: boolean): PermissionObject;
|
||||
public serialize(checkAdmin?: boolean): Required<PermissionObject>;
|
||||
public toArray(checkAdmin?: boolean): PermissionString[];
|
||||
public valueOf(): number;
|
||||
|
||||
public static ALL: number;
|
||||
public static DEFAULT: number;
|
||||
public static FLAGS: PermissionFlags;
|
||||
public static resolve(permission: PermissionResolvable): number;
|
||||
public static resolve(permission?: PermissionResolvable): number;
|
||||
}
|
||||
|
||||
export class Presence {
|
||||
@@ -1730,6 +1747,8 @@ declare module 'discord.js' {
|
||||
|
||||
type Base64String = string;
|
||||
|
||||
type BitFieldResolvable<T extends string> = RecursiveArray<T | number | Readonly<BitField<T>>> | T | number | Readonly<BitField<T>>;
|
||||
|
||||
type BufferResolvable = Buffer | string;
|
||||
|
||||
type ChannelCreationOverwrites = {
|
||||
@@ -2181,7 +2200,7 @@ declare module 'discord.js' {
|
||||
|
||||
interface RecursiveArray<T> extends Array<T | RecursiveArray<T>> { }
|
||||
|
||||
type PermissionResolvable = RecursiveArray<Permissions | PermissionString | number> | Permissions | PermissionString | number;
|
||||
type PermissionResolvable = BitFieldResolvable<PermissionString>
|
||||
|
||||
type PremiumTier = number;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user