From 3ed0c8f293d47b80821d848e1c4395b4a068da5d Mon Sep 17 00:00:00 2001 From: MARCROCK22 Date: Sun, 5 Jan 2025 15:23:41 -0400 Subject: [PATCH] fix: resolve bitfield correctly --- src/commands/handle.ts | 4 ++-- src/commands/handler.ts | 8 ++++---- src/common/shorters/channels.ts | 24 +++++++++++------------ src/structures/GuildMember.ts | 8 ++++---- src/structures/extra/BitField.ts | 25 +++++++++--------------- src/structures/extra/Permissions.ts | 30 ++++++++++++++--------------- tests/bitfield.test.mts | 14 +++++++------- 7 files changed, 52 insertions(+), 61 deletions(-) diff --git a/src/commands/handle.ts b/src/commands/handle.ts index b3a8570..8f6d224 100644 --- a/src/commands/handle.ts +++ b/src/commands/handle.ts @@ -520,9 +520,9 @@ export class HandleCommand { } checkPermissions(app: PermissionsBitField, bot: bigint) { - if (app.has('Administrator')) return; + if (app.has(['Administrator'])) return; - const permissions = app.missings(...app.values([bot])); + const permissions = app.missings(app.values([bot])); if (permissions.length) { return app.keys(permissions); } diff --git a/src/commands/handler.ts b/src/commands/handler.ts index 7ef9dde..4d4c99a 100644 --- a/src/commands/handler.ts +++ b/src/commands/handler.ts @@ -523,14 +523,14 @@ export class CommandHandler extends BaseHandler { option.onPermissionsFail?.bind(option) ?? commandInstance.onPermissionsFail?.bind(commandInstance) ?? this.client.options.commands?.defaults?.onPermissionsFail; - option.botPermissions = PermissionsBitField.resolve( + option.botPermissions = PermissionsBitField.resolve([ option.botPermissions ?? PermissionsBitField.None, commandInstance.botPermissions ?? PermissionsBitField.None, - ); - option.defaultMemberPermissions ??= PermissionsBitField.resolve( + ]); + option.defaultMemberPermissions ??= PermissionsBitField.resolve([ option.defaultMemberPermissions ?? PermissionsBitField.None, commandInstance.defaultMemberPermissions ?? PermissionsBitField.None, - ); + ]); option.contexts ??= commandInstance.contexts; option.integrationTypes ??= commandInstance.integrationTypes; option.props ??= commandInstance.props; diff --git a/src/common/shorters/channels.ts b/src/common/shorters/channels.ts index dfa8445..fa94af8 100644 --- a/src/common/shorters/channels.ts +++ b/src/common/shorters/channels.ts @@ -154,19 +154,19 @@ export class ChannelShorter extends BaseShorter { async memberPermissions(channelId: string, member: GuildMember, checkAdmin = true): Promise { const memberPermissions = await member.fetchPermissions(); - if (checkAdmin && memberPermissions.has(PermissionFlagsBits.Administrator)) { + if (checkAdmin && memberPermissions.has([PermissionFlagsBits.Administrator])) { return new PermissionsBitField(PermissionsBitField.All); } const overwrites = await this.overwritesFor(channelId, member); const permissions = new PermissionsBitField(memberPermissions.bits); - permissions.remove(overwrites.everyone?.deny.bits ?? 0n); - permissions.add(overwrites.everyone?.allow.bits ?? 0n); - permissions.remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny.bits) : 0n); - permissions.add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow.bits) : 0n); - permissions.remove(overwrites.member?.deny.bits ?? 0n); - permissions.add(overwrites.member?.allow.bits ?? 0n); + permissions.remove([overwrites.everyone?.deny.bits ?? 0n]); + permissions.add([overwrites.everyone?.allow.bits ?? 0n]); + permissions.remove(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.deny.bits) : [0n]); + permissions.add(overwrites.roles.length > 0 ? overwrites.roles.map(role => role.allow.bits) : [0n]); + permissions.remove([overwrites.member?.deny.bits ?? 0n]); + permissions.add([overwrites.member?.allow.bits ?? 0n]); return permissions; } @@ -195,7 +195,7 @@ export class ChannelShorter extends BaseShorter { } async rolePermissions(channelId: string, role: GuildRole, checkAdmin = true): Promise { - if (checkAdmin && role.permissions.has(PermissionFlagsBits.Administrator)) { + if (checkAdmin && role.permissions.has([PermissionFlagsBits.Administrator])) { return new PermissionsBitField(PermissionsBitField.All); } const channelOverwrites = (await this.client.cache.overwrites?.get(channelId)) ?? []; @@ -204,10 +204,10 @@ export class ChannelShorter extends BaseShorter { const roleOverwrites = channelOverwrites.find(x => x.id === role.id); const permissions = new PermissionsBitField(role.permissions.bits); - permissions.remove(everyoneOverwrites?.deny.bits ?? 0n); - permissions.add(everyoneOverwrites?.allow.bits ?? 0n); - permissions.remove(roleOverwrites?.deny.bits ?? 0n); - permissions.add(roleOverwrites?.allow.bits ?? 0n); + permissions.remove([everyoneOverwrites?.deny.bits ?? 0n]); + permissions.add([everyoneOverwrites?.allow.bits ?? 0n]); + permissions.remove([roleOverwrites?.deny.bits ?? 0n]); + permissions.add([roleOverwrites?.allow.bits ?? 0n]); return permissions; } diff --git a/src/structures/GuildMember.ts b/src/structures/GuildMember.ts index f723de7..0cc238d 100644 --- a/src/structures/GuildMember.ts +++ b/src/structures/GuildMember.ts @@ -242,18 +242,18 @@ export class GuildMember extends BaseGuildMember { } async bannable(force = false) { - return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('BanMembers'); + return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has(['BanMembers']); } async kickable(force = false) { - return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('KickMembers'); + return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has(['KickMembers']); } async moderatable(force = false) { return ( - !(await this.roles.permissions(force)).has('Administrator') && + !(await this.roles.permissions(force)).has(['Administrator']) && (await this.manageable(force)) && - (await this.__me!.fetchPermissions(force)).has('KickMembers') + (await this.__me!.fetchPermissions(force)).has(['KickMembers']) ); } } diff --git a/src/structures/extra/BitField.ts b/src/structures/extra/BitField.ts index 13a1a04..774ce29 100644 --- a/src/structures/extra/BitField.ts +++ b/src/structures/extra/BitField.ts @@ -1,4 +1,4 @@ -export type BitFieldResolvable = keyof T | number | bigint | (keyof T | number | bigint)[]; +export type BitFieldResolvable = keyof T | number | bigint; export class BitField { static None = 0n; @@ -18,17 +18,17 @@ export class BitField { return this.bit; } - has(...bits: BitFieldResolvable[]) { + has(bits: BitFieldResolvable[]) { const bitsResolved = bits.map(bit => this.resolve(bit)); return bitsResolved.every(bit => (this.bits & bit) === bit); } - missings(...bits: BitFieldResolvable[]) { + missings(bits: BitFieldResolvable[]) { const bitsResolved = bits.map(bit => this.resolve(bit)); return bitsResolved.filter(bit => (this.bits & bit) !== bit); } - equals(other: BitFieldResolvable) { + equals(other: BitFieldResolvable[]) { return this.bits === this.resolve(other); } @@ -54,7 +54,7 @@ export class BitField { }, [] as bigint[]); } - add(...bits: (BitFieldResolvable | undefined)[]) { + add(bits: BitFieldResolvable[]) { for (const bit of bits) { if (!bit) continue; this.bits |= this.resolve(bit); @@ -62,20 +62,20 @@ export class BitField { return this.bits; } - remove(...bits: BitFieldResolvable[]): bigint { + remove(bits: BitFieldResolvable[]): bigint { for (const bit of bits) { this.bits &= ~this.resolve(bit); } return this.bits; } - resolve(...bits: BitFieldResolvable[]): bigint { + resolve(bits: BitFieldResolvable | BitFieldResolvable[]): bigint { let bitsResult = 0n; - for (const bit of bits) { + for (const bit of Array.isArray(bits) ? bits : [bits]) { switch (typeof bit) { case 'string': - bitsResult |= this.resolve(this.Flags[bit]); + bitsResult |= this.resolve([this.Flags[bit]]); break; case 'number': bitsResult |= BigInt(bit); @@ -83,13 +83,6 @@ export class BitField { case 'bigint': bitsResult |= bit; break; - case 'object': { - if (!Array.isArray(bit)) { - throw new TypeError(`Cannot resolve permission: ${bit}`); - } - bitsResult |= bits.reduce((acc, val) => this.resolve(val) | acc, BitField.None); - break; - } default: throw new TypeError(`Cannot resolve permission: ${typeof bit === 'symbol' ? String(bit) : (bit as unknown)}`); } diff --git a/src/structures/extra/Permissions.ts b/src/structures/extra/Permissions.ts index bd914d9..498090e 100644 --- a/src/structures/extra/Permissions.ts +++ b/src/structures/extra/Permissions.ts @@ -6,29 +6,34 @@ export class PermissionsBitField extends BitField { Flags = PermissionFlagsBits; static All = Object.values(PermissionFlagsBits).reduce((acc, value) => acc | value, 0n); - constructor(bitfields?: BitFieldResolvable) { + constructor( + bitfields?: BitFieldResolvable | BitFieldResolvable[], + ) { super(); if (bitfields) this.bit = this.resolve(bitfields); } declare keys: (bits?: BitFieldResolvable[]) => PermissionStrings; - has(...bits: BitFieldResolvable[]) { - return super.has(...bits) || super.has('Administrator'); + has(bits: BitFieldResolvable[]) { + return super.has(bits) || super.has(['Administrator']); } - strictHas(...bits: BitFieldResolvable[]) { - return super.has(...bits); + strictHas(bits: BitFieldResolvable[]) { + return super.has(bits); } - resolve(...bits: BitFieldResolvable[]): bigint { - return bits.reduce((acc, cur) => acc | PermissionsBitField.resolve(cur), BitField.None); + resolve(bits: BitFieldResolvable | BitFieldResolvable[]): bigint { + return (Array.isArray(bits) ? bits : [bits]).reduce( + (acc, cur) => acc | PermissionsBitField.resolve([cur]), + BitField.None, + ); } - static resolve(...bits: BitFieldResolvable[]): bigint { + static resolve(bits: BitFieldResolvable | BitFieldResolvable[]): bigint { let bitsResult = 0n; - for (const bit of bits) { + for (const bit of Array.isArray(bits) ? bits : [bits]) { switch (typeof bit) { case 'string': bitsResult |= PermissionsBitField.resolve(PermissionFlagsBits[bit as keyof typeof PermissionFlagsBits]); @@ -39,13 +44,6 @@ export class PermissionsBitField extends BitField { case 'bigint': bitsResult |= bit; break; - case 'object': { - if (!Array.isArray(bit)) { - throw new TypeError(`Cannot resolve permission: ${bit}`); - } - bitsResult |= bit.reduce((acc, val) => PermissionsBitField.resolve(val) | acc, BitField.None); - break; - } default: throw new TypeError(`Cannot resolve permission: ${typeof bit === 'symbol' ? String(bit) : (bit as any)}`); } diff --git a/tests/bitfield.test.mts b/tests/bitfield.test.mts index c444a36..94510ef 100644 --- a/tests/bitfield.test.mts +++ b/tests/bitfield.test.mts @@ -12,7 +12,7 @@ describe('PermissionsBitField', () => { test('add', () => { const p = new PermissionsBitField(['CreateEvents']); p.add(['AttachFiles']); - p.add('ChangeNickname'); + p.add(['ChangeNickname']); assert.equal( p.bits, PermissionFlagsBits.CreateEvents | PermissionFlagsBits.AttachFiles | PermissionFlagsBits.ChangeNickname, @@ -21,14 +21,14 @@ describe('PermissionsBitField', () => { test('remove', () => { const p = new PermissionsBitField(['CreateEvents']); - p.remove('CreateEvents'); + p.remove(['CreateEvents']); assert.equal(p.bits, BitField.None); }); test('keys', () => { const p = new PermissionsBitField(['CreateEvents', 'Administrator']); p.add(['AttachFiles']); - p.add('ChangeNickname'); + p.add(['ChangeNickname']); const keys = ['CreateEvents', 'Administrator', 'AttachFiles', 'ChangeNickname']; assert.equal( @@ -40,7 +40,7 @@ describe('PermissionsBitField', () => { test('values', () => { const p = new PermissionsBitField(['CreateEvents']); - p.add('Administrator'); + p.add(['Administrator']); p.add(['ChangeNickname']); assert.deepEqual(p.values(), [ PermissionFlagsBits.Administrator, @@ -51,15 +51,15 @@ describe('PermissionsBitField', () => { test('missings', () => { const p = new PermissionsBitField(['CreateEvents']); - p.add('Administrator'); + p.add(['Administrator']); p.add(['ChangeNickname']); - assert.deepEqual(p.missings('Connect'), [PermissionFlagsBits.Connect]); + assert.deepEqual(p.missings(['Connect']), [PermissionFlagsBits.Connect]); }); test('equals', () => { const p = new PermissionsBitField(['CreateEvents']); p.add(['ChangeNickname']); assert.deepEqual(p.equals(['ChangeNickname', 'CreateEvents']), true); - assert.deepEqual(p.equals('Connect'), false); + assert.deepEqual(p.equals(['Connect']), false); }); });