fix: resolve bitfield correctly

This commit is contained in:
MARCROCK22 2025-01-05 15:23:41 -04:00
parent cb52d6fe6b
commit 3ed0c8f293
7 changed files with 52 additions and 61 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -154,19 +154,19 @@ export class ChannelShorter extends BaseShorter {
async memberPermissions(channelId: string, member: GuildMember, checkAdmin = true): Promise<PermissionsBitField> {
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<PermissionsBitField> {
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;
}

View File

@ -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'])
);
}
}

View File

@ -1,4 +1,4 @@
export type BitFieldResolvable<T extends object> = keyof T | number | bigint | (keyof T | number | bigint)[];
export type BitFieldResolvable<T extends object> = keyof T | number | bigint;
export class BitField<T extends object> {
static None = 0n;
@ -18,17 +18,17 @@ export class BitField<T extends object> {
return this.bit;
}
has(...bits: BitFieldResolvable<T>[]) {
has(bits: BitFieldResolvable<T>[]) {
const bitsResolved = bits.map(bit => this.resolve(bit));
return bitsResolved.every(bit => (this.bits & bit) === bit);
}
missings(...bits: BitFieldResolvable<T>[]) {
missings(bits: BitFieldResolvable<T>[]) {
const bitsResolved = bits.map(bit => this.resolve(bit));
return bitsResolved.filter(bit => (this.bits & bit) !== bit);
}
equals(other: BitFieldResolvable<T>) {
equals(other: BitFieldResolvable<T>[]) {
return this.bits === this.resolve(other);
}
@ -54,7 +54,7 @@ export class BitField<T extends object> {
}, [] as bigint[]);
}
add(...bits: (BitFieldResolvable<T> | undefined)[]) {
add(bits: BitFieldResolvable<T>[]) {
for (const bit of bits) {
if (!bit) continue;
this.bits |= this.resolve(bit);
@ -62,20 +62,20 @@ export class BitField<T extends object> {
return this.bits;
}
remove(...bits: BitFieldResolvable<T>[]): bigint {
remove(bits: BitFieldResolvable<T>[]): bigint {
for (const bit of bits) {
this.bits &= ~this.resolve(bit);
}
return this.bits;
}
resolve(...bits: BitFieldResolvable<T>[]): bigint {
resolve(bits: BitFieldResolvable<T> | BitFieldResolvable<T>[]): 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<T extends object> {
case 'bigint':
bitsResult |= bit;
break;
case 'object': {
if (!Array.isArray(bit)) {
throw new TypeError(`Cannot resolve permission: ${bit}`);
}
bitsResult |= bits.reduce<bigint>((acc, val) => this.resolve(val) | acc, BitField.None);
break;
}
default:
throw new TypeError(`Cannot resolve permission: ${typeof bit === 'symbol' ? String(bit) : (bit as unknown)}`);
}

View File

@ -6,29 +6,34 @@ export class PermissionsBitField extends BitField<typeof PermissionFlagsBits> {
Flags = PermissionFlagsBits;
static All = Object.values(PermissionFlagsBits).reduce((acc, value) => acc | value, 0n);
constructor(bitfields?: BitFieldResolvable<typeof PermissionFlagsBits>) {
constructor(
bitfields?: BitFieldResolvable<typeof PermissionFlagsBits> | BitFieldResolvable<typeof PermissionFlagsBits>[],
) {
super();
if (bitfields) this.bit = this.resolve(bitfields);
}
declare keys: (bits?: BitFieldResolvable<typeof PermissionFlagsBits>[]) => PermissionStrings;
has(...bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) {
return super.has(...bits) || super.has('Administrator');
has(bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) {
return super.has(bits) || super.has(['Administrator']);
}
strictHas(...bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) {
return super.has(...bits);
strictHas(bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) {
return super.has(bits);
}
resolve<T extends typeof PermissionFlagsBits>(...bits: BitFieldResolvable<T>[]): bigint {
return bits.reduce<bigint>((acc, cur) => acc | PermissionsBitField.resolve(cur), BitField.None);
resolve<T extends typeof PermissionFlagsBits>(bits: BitFieldResolvable<T> | BitFieldResolvable<T>[]): bigint {
return (Array.isArray(bits) ? bits : [bits]).reduce<bigint>(
(acc, cur) => acc | PermissionsBitField.resolve([cur]),
BitField.None,
);
}
static resolve<T extends typeof PermissionFlagsBits>(...bits: BitFieldResolvable<T>[]): bigint {
static resolve<T extends typeof PermissionFlagsBits>(bits: BitFieldResolvable<T> | BitFieldResolvable<T>[]): 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<typeof PermissionFlagsBits> {
case 'bigint':
bitsResult |= bit;
break;
case 'object': {
if (!Array.isArray(bit)) {
throw new TypeError(`Cannot resolve permission: ${bit}`);
}
bitsResult |= bit.reduce<bigint>((acc, val) => PermissionsBitField.resolve(val) | acc, BitField.None);
break;
}
default:
throw new TypeError(`Cannot resolve permission: ${typeof bit === 'symbol' ? String(bit) : (bit as any)}`);
}

View File

@ -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);
});
});