diff --git a/src/commands/decorators.ts b/src/commands/decorators.ts index cfdbf83..adf701b 100644 --- a/src/commands/decorators.ts +++ b/src/commands/decorators.ts @@ -1,11 +1,11 @@ import type { FlatObjectKeys, PermissionStrings } from '../common'; +import { PermissionsBitField } from '../structures/extra/Permissions'; import { ApplicationCommandType, ApplicationIntegrationType, type EntryPointCommandHandlerType, InteractionContextType, type LocaleString, - PermissionFlagsBits, } from '../types'; import type { CommandOption, OptionsRecord, SubCommand } from './applications/chat'; import type { DefaultLocale, ExtraProps, IgnoreCommand, MiddlewareContext } from './applications/shared'; @@ -159,11 +159,11 @@ export function Declare(declare: CommandDeclareOptions) { integrationTypes = declare.integrationTypes?.map(i => ApplicationIntegrationType[i]) ?? [ ApplicationIntegrationType.GuildInstall, ]; - defaultMemberPermissions = Array.isArray(declare.defaultMemberPermissions) - ? declare.defaultMemberPermissions?.reduce((acc, prev) => acc | PermissionFlagsBits[prev], BigInt(0)) + defaultMemberPermissions = declare.defaultMemberPermissions + ? PermissionsBitField.resolve(declare.defaultMemberPermissions) : declare.defaultMemberPermissions; - botPermissions = Array.isArray(declare.botPermissions) - ? declare.botPermissions?.reduce((acc, prev) => acc | PermissionFlagsBits[prev], BigInt(0)) + botPermissions = declare.botPermissions + ? PermissionsBitField.resolve(declare.botPermissions) : declare.botPermissions; description = ''; type: ApplicationCommandType = ApplicationCommandType.ChatInput; diff --git a/src/commands/handle.ts b/src/commands/handle.ts index 721b93f..8558b7d 100644 --- a/src/commands/handle.ts +++ b/src/commands/handle.ts @@ -169,10 +169,18 @@ export class HandleCommand { resolver: OptionResolverStructure, context: CommandContext, ) { - if (context.guildId && command.botPermissions && interaction.appPermissions) { - const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions); - if (permissions) return command.onBotPermissionsFail?.(context, permissions); + if (context.guildId && interaction.appPermissions) { + if (command.botPermissions) { + const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions); + if (permissions) return command.onBotPermissionsFail?.(context, permissions); + } + + if (command.defaultMemberPermissions) { + const permissions = this.checkPermissions(interaction.member!.permissions, command.defaultMemberPermissions); + if (permissions) return command.onPermissionsFail?.(context, permissions); + } } + if (!(await this.runOptions(command, context, resolver))) return; const resultGlobal = await this.runGlobalMiddlewares(command, context); diff --git a/src/commands/handler.ts b/src/commands/handler.ts index ce58017..a649f41 100644 --- a/src/commands/handler.ts +++ b/src/commands/handler.ts @@ -3,6 +3,7 @@ import { basename, dirname } from 'node:path'; import type { EntryPointCommand } from '.'; import type { Logger, MakeRequired, NulleableCoalising, OmitInsert } from '../common'; import { BaseHandler, isCloudfareWorker } from '../common'; +import { PermissionsBitField } from '../structures/extra/Permissions'; import { type APIApplicationCommandChannelOption, type APIApplicationCommandIntegerOption, @@ -483,8 +484,14 @@ export class CommandHandler extends BaseHandler { option.onPermissionsFail?.bind(option) ?? commandInstance.onPermissionsFail?.bind(commandInstance) ?? this.client.options.commands?.defaults?.onPermissionsFail; - option.botPermissions ??= commandInstance.botPermissions; - option.defaultMemberPermissions ??= commandInstance.defaultMemberPermissions; + option.botPermissions = PermissionsBitField.add( + option.botPermissions ?? PermissionsBitField.None, + commandInstance.botPermissions, + ); + option.defaultMemberPermissions ??= PermissionsBitField.add( + option.defaultMemberPermissions ?? PermissionsBitField.None, + commandInstance.defaultMemberPermissions, + ); option.contexts ??= commandInstance.contexts; option.integrationTypes ??= commandInstance.integrationTypes; option.props ??= commandInstance.props; diff --git a/src/structures/extra/BitField.ts b/src/structures/extra/BitField.ts index 8606003..745fd9b 100644 --- a/src/structures/extra/BitField.ts +++ b/src/structures/extra/BitField.ts @@ -18,24 +18,19 @@ export class BitField { return this.bit; } - add(...bits: BitFieldResolvable[]): bigint { - let reduced = BitField.None; - + add(...bits: (BitFieldResolvable | undefined)[]): bigint { for (const bit of bits) { - reduced |= this.resolve(bit); + if (!bit) continue; + this.bit |= this.resolve(bit); } - - return (this.bit |= reduced); + return this.bit; } remove(...bits: BitFieldResolvable[]): bigint { - let reduced = BitField.None; - for (const bit of bits) { - reduced |= this.resolve(bit); + this.bit &= ~this.resolve(bit); } - - return (this.bit &= ~reduced); + return this.bit; } has(...bits: BitFieldResolvable[]) { @@ -52,27 +47,17 @@ export class BitField { return this.bits === this.resolve(other); } - resolve(bits?: BitFieldResolvable): bigint { + resolve(bits: BitFieldResolvable): bigint { switch (typeof bits) { - case 'number': - return BigInt(bits); case 'string': - return this.resolve(this.Flags[bits]); - case 'bigint': - return bits; - case 'object': { - if (!Array.isArray(bits)) { - throw new TypeError(`Cannot resolve permission: ${bits}`); - } - return bits.map(x => this.resolve(x)).reduce((acc, cur) => acc | cur, BitField.None); - } + return BitField.resolve(this.Flags[bits]); default: - throw new TypeError(`Cannot resolve permission: ${typeof bits === 'symbol' ? String(bits) : (bits as any)}`); + return BitField.resolve(bits); } } keys(bits: BitFieldResolvable[] = [this.bits]) { - const bitsResolved = bits.map(bit => BigInt(this.resolve(bit))); + const bitsResolved = bits.map(bit => this.resolve(bit)); return Object.entries(this.Flags).reduce((acc, value) => { if (bitsResolved.some(bit => (bit & value[1]) === value[1])) { acc.push(value[0]); @@ -83,7 +68,7 @@ export class BitField { } values(bits: BitFieldResolvable[] = [this.bits]) { - const bitsResolved = bits.map(bit => BigInt(this.resolve(bit))); + const bitsResolved = bits.map(bit => this.resolve(bit)); return Object.entries(this.Flags).reduce((acc, value) => { if (bitsResolved.some(bit => (bit & value[1]) === value[1])) { acc.push(value[1]); @@ -92,4 +77,40 @@ export class BitField { return acc; }, [] as bigint[]); } + + static add(base: BitFieldResolvable, ...bits: (BitFieldResolvable | undefined)[]) { + base = BitField.resolve(base); + + for (const bit of bits) { + if (!bit) continue; + base |= BitField.resolve(bit); + } + return base; + } + + static remove(base: BitFieldResolvable, ...bits: BitFieldResolvable[]): bigint { + base = BitField.resolve(base); + + for (const bit of bits) { + base &= ~BitField.resolve(bit); + } + return base; + } + + static resolve(bits: BitFieldResolvable): bigint { + switch (typeof bits) { + case 'number': + return BigInt(bits); + case 'bigint': + return bits; + case 'object': { + if (!Array.isArray(bits)) { + throw new TypeError(`Cannot resolve permission: ${bits}`); + } + return bits.map(x => BitField.resolve(x)).reduce((acc, cur) => acc | cur, BitField.None); + } + default: + throw new TypeError(`Cannot resolve permission: ${typeof bits === 'symbol' ? String(bits) : (bits as any)}`); + } + } } diff --git a/src/structures/extra/Permissions.ts b/src/structures/extra/Permissions.ts index 87c27a2..e13155c 100644 --- a/src/structures/extra/Permissions.ts +++ b/src/structures/extra/Permissions.ts @@ -20,4 +20,13 @@ export class PermissionsBitField extends BitField { strictHas(...bits: BitFieldResolvable[]) { return super.has(...bits); } + + static resolve(bits: BitFieldResolvable): bigint { + switch (typeof bits) { + case 'string': + return PermissionsBitField.resolve(PermissionFlagsBits[bits as keyof typeof PermissionFlagsBits]); + default: + return BitField.resolve(bits); + } + } }