diff --git a/src/client/oninteractioncreate.ts b/src/client/oninteractioncreate.ts index 8b1e058..b8eca26 100644 --- a/src/client/oninteractioncreate.ts +++ b/src/client/oninteractioncreate.ts @@ -30,7 +30,7 @@ export async function onInteractionCreate( { const parentCommand = self.commands?.values.find(x => { if (body.data.guild_id) { - return x.guild_id?.includes(body.data.guild_id) && x.name === body.data.name; + return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name; } return x.name === body.data.name; }); @@ -78,7 +78,7 @@ export async function onInteractionCreate( { const command = self.commands?.values.find(x => { if (body.data.guild_id) { - return x.guild_id?.includes(body.data.guild_id) && x.name === body.data.name; + return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name; } return x.name === body.data.name; }) as ContextMenuCommand; @@ -96,8 +96,8 @@ export async function onInteractionCreate( const permissions = interaction.appPermissions.missings( ...interaction.appPermissions.values([command.botPermissions]), ); - if (permissions.length) { - return command.onPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); + if (!interaction.appPermissions.has('Administrator') && permissions.length) { + return command.onBotPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); } } const resultRunGlobalMiddlewares = await command.__runGlobalMiddlewares(context); @@ -136,7 +136,7 @@ export async function onInteractionCreate( { const parentCommand = self.commands?.values.find(x => { if (body.data.guild_id) { - return x.guild_id?.includes(body.data.guild_id) && x.name === body.data.name; + return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name; } return x.name === body.data.name; }); @@ -159,8 +159,8 @@ export async function onInteractionCreate( const permissions = interaction.appPermissions.missings( ...interaction.appPermissions.values([command.botPermissions]), ); - if (permissions.length) { - return command.onPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); + if (!interaction.appPermissions.has('Administrator') && permissions.length) { + return command.onBotPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); } } const [erroredOptions, result] = await command.__runOptions(context, optionsResolver); diff --git a/src/client/onmessagecreate.ts b/src/client/onmessagecreate.ts index 206ad91..feca078 100644 --- a/src/client/onmessagecreate.ts +++ b/src/client/onmessagecreate.ts @@ -91,7 +91,7 @@ export async function onMessageCreate( if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`); if (!command.contexts?.includes(InteractionContextTypes.BOT_DM) && !message.guildId) return; - if (command.guild_id && !command.guild_id?.includes(message.guildId!)) return; + if (command.guildId && !command.guildId?.includes(message.guildId!)) return; const resolved: MakeRequired = { channels: {}, @@ -108,13 +108,20 @@ export async function onMessageCreate( const extendContext = self.options?.context?.(message) ?? {}; Object.assign(context, extendContext); try { + if (command.defaultMemberPermissions && message.guildId) { + const memberPermissions = await self.members.permissions(message.guildId, message.author.id); + const permissions = memberPermissions.missings(...memberPermissions.values([command.defaultMemberPermissions])); + if (!memberPermissions.has('Administrator') && permissions.length) { + return command.onPermissionsFail?.(context, memberPermissions.keys(permissions)); + } + } if (command.botPermissions && message.guildId) { const meMember = await self.cache.members?.get(self.botId, message.guildId); if (!meMember) return; //enable member cache and "Guilds" intent, lol const appPermissions = await meMember.fetchPermissions(); const permissions = appPermissions.missings(...appPermissions.values([command.botPermissions])); - if (permissions.length) { - return command.onPermissionsFail?.(context, appPermissions.keys(permissions)); + if (!appPermissions.has('Administrator') && permissions.length) { + return command.onBotPermissionsFail?.(context, appPermissions.keys(permissions)); } } if (errors.length) { diff --git a/src/commands/applications/chat.ts b/src/commands/applications/chat.ts index 49b333e..3cf5a9b 100644 --- a/src/commands/applications/chat.ts +++ b/src/commands/applications/chat.ts @@ -1,14 +1,14 @@ import { + ApplicationCommandOptionType, + ApplicationCommandType, type APIApplicationCommandBasicOption, type APIApplicationCommandOption, type APIApplicationCommandSubcommandGroupOption, - ApplicationCommandOptionType, - ApplicationCommandType, type LocaleString, } from 'discord-api-types/v10'; import type { PermissionStrings, SeyfertNumberOption, SeyfertStringOption } from '../..'; import type { Attachment } from '../../builders'; -import { type FlatObjectKeys, magicImport } from '../../common'; +import { magicImport, type FlatObjectKeys } from '../../common'; import type { AllChannels, AutocompleteInteraction, GuildRole, InteractionGuildMember, User } from '../../structures'; import type { Groups, IntegrationTypes, InteractionContextTypes, RegisteredMiddlewares } from '../decorators'; import type { OptionResolver } from '../optionresolver'; @@ -117,13 +117,13 @@ class BaseCommand { } >; - guild_id?: string[]; + guildId?: string[]; name!: string; type!: number; // ApplicationCommandType.ChatInput | ApplicationCommandOptionType.Subcommand nsfw?: boolean; description!: string; - default_member_permissions?: string; - integration_types?: IntegrationTypes[]; + defaultMemberPermissions?: bigint; + integrationTypes?: IntegrationTypes[]; contexts?: InteractionContextTypes[]; botPermissions?: bigint; name_localizations?: Partial>; @@ -247,10 +247,10 @@ class BaseCommand { description: this.description, name_localizations: this.name_localizations, description_localizations: this.description_localizations, - guild_id: this.guild_id, - default_member_permissions: this.default_member_permissions, + guild_id: this.guildId, + default_member_permissions: this.defaultMemberPermissions ? this.defaultMemberPermissions.toString() : '', contexts: this.contexts, - integration_types: this.integration_types, + integration_types: this.integrationTypes, } as { name: BaseCommand['name']; type: BaseCommand['type']; @@ -258,10 +258,10 @@ class BaseCommand { description: BaseCommand['description']; name_localizations: BaseCommand['name_localizations']; description_localizations: BaseCommand['description_localizations']; - guild_id: BaseCommand['guild_id']; - default_member_permissions: BaseCommand['default_member_permissions']; + guild_id: BaseCommand['guildId']; + default_member_permissions: string; contexts: BaseCommand['contexts']; - integration_types: BaseCommand['integration_types']; + integration_types: BaseCommand['integrationTypes']; }; return data; } @@ -284,6 +284,7 @@ class BaseCommand { onRunError?(context: CommandContext, error: unknown): any; onOptionsError?(context: CommandContext<{}, never>, metadata: OnOptionsReturnObject): any; onMiddlewaresError?(context: CommandContext<{}, never>, error: string): any; + onBotPermissionsFail?(context: CommandContext<{}, never>, permissions: PermissionStrings): any; onPermissionsFail?(context: CommandContext<{}, never>, permissions: PermissionStrings): any; onInternalError?(client: UsingClient, error?: unknown): any; } @@ -333,6 +334,9 @@ export class Command extends BaseCommand { onMiddlewaresError(context: CommandContext<{}, never>, error: string): any { context.client.logger.fatal(`${this.name}.`, context.author.id, error); } + onBotPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any { + context.client.logger.fatal(`${this.name}.`, context.author.id, permissions); + } onPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any { context.client.logger.fatal(`${this.name}.`, context.author.id, permissions); } diff --git a/src/commands/applications/menu.ts b/src/commands/applications/menu.ts index d4cc165..cf4880c 100644 --- a/src/commands/applications/menu.ts +++ b/src/commands/applications/menu.ts @@ -1,5 +1,5 @@ import type { ApplicationCommandType, LocaleString } from 'discord-api-types/v10'; -import { type PermissionStrings, magicImport } from '../../common'; +import { magicImport, type PermissionStrings } from '../../common'; import type { IntegrationTypes, InteractionContextTypes, RegisteredMiddlewares } from '../decorators'; import type { MenuCommandContext } from './menucontext'; import type { PassFunction, StopFunction, UsingClient } from './shared'; @@ -10,14 +10,14 @@ export abstract class ContextMenuCommand { __filePath?: string; __t?: { name: string | undefined; description: string | undefined }; - guild_id?: string[]; + guildId?: string[]; name!: string; type!: ApplicationCommandType.User | ApplicationCommandType.Message; nsfw?: boolean; - integration_types?: IntegrationTypes[]; + integrationTypes?: IntegrationTypes[]; contexts?: InteractionContextTypes[]; description!: string; - default_member_permissions?: string; + defaultMemberPermissions?: string; botPermissions?: bigint; dm?: boolean; name_localizations?: Partial>; @@ -91,11 +91,11 @@ export abstract class ContextMenuCommand { description: this.description, name_localizations: this.name_localizations, description_localizations: this.description_localizations, - guild_id: this.guild_id, + guild_id: this.guildId, dm_permission: this.dm, - default_member_permissions: this.default_member_permissions, + default_member_permissions: this.defaultMemberPermissions, contexts: this.contexts, - integration_types: this.integration_types, + integration_types: this.integrationTypes, }; } @@ -114,6 +114,9 @@ export abstract class ContextMenuCommand { onMiddlewaresError(context: MenuCommandContext, error: string): any { context.client.logger.fatal(`${this.name}.`, context.author.id, error); } + onBotPermissionsFail(context: MenuCommandContext, permissions: PermissionStrings): any { + context.client.logger.fatal(`${this.name}.`, context.author.id, permissions); + } onPermissionsFail(context: MenuCommandContext, permissions: PermissionStrings): any { context.client.logger.fatal(`${this.name}.`, context.author.id, permissions); } diff --git a/src/commands/decorators.ts b/src/commands/decorators.ts index 55effd1..b9e4462 100644 --- a/src/commands/decorators.ts +++ b/src/commands/decorators.ts @@ -1,4 +1,4 @@ -import { ApplicationCommandType, type LocaleString, PermissionFlagsBits } from 'discord-api-types/v10'; +import { ApplicationCommandType, PermissionFlagsBits, type LocaleString } from 'discord-api-types/v10'; import type { FlatObjectKeys, PermissionStrings } from '../common'; import type { CommandOption, OptionsRecord, SubCommand } from './applications/chat'; import type { DefaultLocale, MiddlewareContext } from './applications/shared'; @@ -140,21 +140,21 @@ export function Declare(declare: DeclareOptions) { name = declare.name; nsfw = declare.nsfw; contexts = declare.contexts?.map(i => InteractionContextTypes[i]); - integration_types = declare.integrationTypes?.map(i => IntegrationTypes[i]); - default_member_permissions = Array.isArray(declare.defaultMemberPermissions) - ? declare.defaultMemberPermissions?.reduce((acc, prev) => acc | PermissionFlagsBits[prev], BigInt(0)).toString() + integrationTypes = declare.integrationTypes?.map(i => IntegrationTypes[i]); + defaultMemberPermissions = Array.isArray(declare.defaultMemberPermissions) + ? declare.defaultMemberPermissions?.reduce((acc, prev) => acc | PermissionFlagsBits[prev], BigInt(0)) : declare.defaultMemberPermissions; botPermissions = Array.isArray(declare.botPermissions) ? declare.botPermissions?.reduce((acc, prev) => acc | PermissionFlagsBits[prev], BigInt(0)) : declare.botPermissions; description = ''; type: ApplicationCommandType = ApplicationCommandType.ChatInput; - guild_id?: string[]; + guildId?: string[]; constructor(...args: any[]) { super(...args); if ('description' in declare) this.description = declare.description; if ('type' in declare) this.type = declare.type; - if ('guildId' in declare) this.guild_id = declare.guildId; + if ('guildId' in declare) this.guildId = declare.guildId; // check if all properties are valid } }; diff --git a/src/commands/handler.ts b/src/commands/handler.ts index 2365666..a3204fd 100644 --- a/src/commands/handler.ts +++ b/src/commands/handler.ts @@ -109,6 +109,8 @@ export class CommandHandler extends BaseHandler { option.onInternalError = option.onInternalError?.bind(option) ?? commandInstance.onInternalError?.bind(commandInstance); option.onAfterRun = option.onAfterRun?.bind(option) ?? commandInstance.onAfterRun?.bind(commandInstance); + option.onBotPermissionsFail = + option.onBotPermissionsFail?.bind(option) ?? commandInstance.onBotPermissionsFail?.bind(commandInstance); option.onPermissionsFail = option.onPermissionsFail?.bind(option) ?? commandInstance.onPermissionsFail?.bind(commandInstance); } diff --git a/src/structures/extra/Permissions.ts b/src/structures/extra/Permissions.ts index f3e67b3..8d3be0d 100644 --- a/src/structures/extra/Permissions.ts +++ b/src/structures/extra/Permissions.ts @@ -6,7 +6,7 @@ export class PermissionsBitField extends BitField { Flags = PermissionFlagsBits; static All = Object.values(PermissionFlagsBits).reduce((acc, value) => acc | value, 0n); - declare keys: (bits: BitFieldResolvable[]) => PermissionStrings; + declare keys: (bits?: BitFieldResolvable[]) => PermissionStrings; has(...bits: BitFieldResolvable[]) { return super.has(...bits) || super.has('Administrator');