aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/extensions/discord-akairo/BotArgumentTypeCaster.ts2
-rw-r--r--lib/extensions/discord-akairo/BotCommand.ts92
-rw-r--r--lib/extensions/discord-akairo/BotCommandHandler.ts107
-rw-r--r--lib/extensions/discord-akairo/BotInhibitor.ts33
-rw-r--r--lib/extensions/discord-akairo/BotListener.ts50
-rw-r--r--lib/extensions/discord-akairo/BotListenerHandler.ts16
-rw-r--r--lib/extensions/discord-akairo/TanzaniteClient.ts21
-rw-r--r--lib/extensions/discord.js/BotClientEvents.ts31
-rw-r--r--lib/extensions/discord.js/ExtendedGuild.ts21
-rw-r--r--lib/extensions/discord.js/ExtendedGuildMember.ts69
-rw-r--r--lib/utils/Arg.ts8
-rw-r--r--lib/utils/BotClientUtils.ts8
-rw-r--r--lib/utils/Constants.ts220
-rw-r--r--lib/utils/Format.ts2
-rw-r--r--lib/utils/Utils.ts88
15 files changed, 515 insertions, 253 deletions
diff --git a/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts b/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
index 5f4f32f..d7943fc 100644
--- a/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
+++ b/lib/extensions/discord-akairo/BotArgumentTypeCaster.ts
@@ -1,3 +1,3 @@
-import { type CommandMessage } from '#lib';
+import type { CommandMessage } from '#lib';
export type BotArgumentTypeCaster<R = unknown> = (message: CommandMessage, phrase: string) => R;
diff --git a/lib/extensions/discord-akairo/BotCommand.ts b/lib/extensions/discord-akairo/BotCommand.ts
index abd945e..11a8bad 100644
--- a/lib/extensions/discord-akairo/BotCommand.ts
+++ b/lib/extensions/discord-akairo/BotCommand.ts
@@ -1,17 +1,16 @@
import { type DiscordEmojiInfo, type RoleWithDuration } from '#args';
-import {
- type BotArgumentTypeCaster,
- type BotCommandHandler,
- type BotInhibitor,
- type BotListener,
- type BotTask,
- type ParsedDuration,
- type TanzaniteClient
+import type {
+ BotArgumentTypeCaster,
+ BotCommandHandler,
+ BotInhibitor,
+ BotListener,
+ BotTask,
+ ParsedDuration,
+ TanzaniteClient
} from '#lib';
import {
- ArgumentMatch,
Command,
- CommandUtil,
+ CommandArguments,
type AkairoApplicationCommandAutocompleteOption,
type AkairoApplicationCommandChannelOptionData,
type AkairoApplicationCommandChoicesData,
@@ -20,26 +19,25 @@ import {
type AkairoApplicationCommandOptionData,
type AkairoApplicationCommandSubCommandData,
type AkairoApplicationCommandSubGroupData,
+ type ArgumentMatch,
type ArgumentOptions,
type ArgumentType,
type ArgumentTypeCaster,
type BaseArgumentType,
type CommandOptions,
+ type CommandUtil,
type ContextMenuCommand,
- type MissingPermissionSupplier,
type SlashOption,
type SlashResolveType
} from 'discord-akairo';
import {
- Message,
PermissionsBitField,
- User,
type ApplicationCommandOptionChoiceData,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
type ApplicationCommandOptionType,
- type PermissionResolvable,
+ type Message,
type PermissionsString,
- type Snowflake
+ type Snowflake,
+ type User
} from 'discord.js';
import _ from 'lodash';
import { SlashMessage } from './SlashMessage.js';
@@ -219,6 +217,7 @@ export type CustomMissingPermissionSupplier = (message: CommandMessage | SlashMe
interface ExtendedCommandOptions {
/**
* Whether the command is hidden from the help command.
+ * @default false
*/
hidden?: boolean;
@@ -244,11 +243,13 @@ interface ExtendedCommandOptions {
/**
* A fake command, completely hidden from the help command.
+ * @default false
*/
pseudo?: boolean;
/**
* Allow this command to be run in channels that are blacklisted.
+ * @default false
*/
bypassChannelBlacklist?: boolean;
@@ -261,6 +262,24 @@ interface ExtendedCommandOptions {
* Extra information about the command, displayed in the help command.
*/
note?: string;
+
+ /**
+ * Whether to check for channel overrides when considering client permissions.
+ * @default false
+ */
+ clientCheckChannel?: boolean;
+
+ /**
+ * Whether to check for channel overrides when considering user permissions.
+ * @default false
+ */
+ userCheckChannel?: boolean;
+
+ /**
+ * **Text Command Only**: Don't check if the user has send permissions in the channel.
+ * @default false
+ */
+ skipSendCheck?: boolean;
}
export interface BaseBotCommandOptions
@@ -281,12 +300,12 @@ export interface BaseBotCommandOptions
/**
* Permissions required by the client to run this command.
*/
- clientPermissions: bigint | bigint[] | CustomMissingPermissionSupplier;
+ clientPermissions: PermissionsString[];
/**
* Permissions required by the user to run this command.
*/
- userPermissions: bigint | bigint[] | CustomMissingPermissionSupplier;
+ userPermissions: PermissionsString[];
/**
* Whether the argument is only accessible to the owners.
@@ -364,6 +383,8 @@ export abstract class BotCommand extends Command {
public declare client: TanzaniteClient;
public declare handler: BotCommandHandler;
public declare description: string;
+ public declare userPermissions: PermissionsString[];
+ public declare clientPermissions: PermissionsString[];
/**
* Show how to use the command.
@@ -411,7 +432,7 @@ export abstract class BotCommand extends Command {
public bypassChannelBlacklist: boolean;
/**
- * Info about the arguments for the help command.
+ * Information about the arguments for the help command.
*/
public argsInfo?: ArgsInfo[];
@@ -420,6 +441,24 @@ export abstract class BotCommand extends Command {
*/
public note?: string;
+ /**
+ * Whether to check for channel overrides when considering client permissions.
+ * @default true
+ */
+ public clientCheckChannel: boolean;
+
+ /**
+ * Whether to check for channel overrides when considering user permissions.
+ * @default true
+ */
+ public userCheckChannel: boolean;
+
+ /**
+ * **Text Command Only**: Don't check if the user has send permissions in the channel.
+ * @default false
+ */
+ public skipSendCheck: boolean;
+
public constructor(id: string, options: CustomCommandOptions) {
const options_ = options as BaseBotCommandOptions;
@@ -490,7 +529,7 @@ export abstract class BotCommand extends Command {
if (newTextArgs.length > 0) newOptions.args = newTextArgs;
if (newSlashArgs.length > 0) newOptions.slashOptions = options_.slashOptions ?? newSlashArgs;
} else if (key === 'clientPermissions' || key === 'userPermissions') {
- newOptions[key] = options_[key] as PermissionResolvable | PermissionResolvable[] | MissingPermissionSupplier;
+ newOptions[key] = options_[key];
} else {
newOptions[key] = options_[key];
}
@@ -543,12 +582,15 @@ export abstract class BotCommand extends Command {
this.examples = options_.examples;
this.options = options_;
this.parsedOptions = newOptions;
- this.hidden = !!options_.hidden;
+ this.hidden = options_.hidden ?? false;
this.restrictedChannels = options_.restrictedChannels;
this.restrictedGuilds = options_.restrictedGuilds;
- this.pseudo = !!options_.pseudo;
- this.bypassChannelBlacklist = !!options_.bypassChannelBlacklist;
+ this.pseudo = options_.pseudo ?? false;
+ this.bypassChannelBlacklist = options_.bypassChannelBlacklist ?? false;
this.note = options_.note;
+ this.clientCheckChannel = options_.clientCheckChannel ?? false;
+ this.userCheckChannel = options_.userCheckChannel ?? false;
+ this.skipSendCheck = options_.skipSendCheck ?? false;
}
/**
@@ -556,13 +598,13 @@ export abstract class BotCommand extends Command {
* @param message - Message that triggered the command.
* @param args - Evaluated arguments.
*/
- public abstract override exec(message: CommandMessage, args: any): any;
+ public abstract override exec(message: CommandMessage, args: CommandArguments): any;
/**
* Executes the command.
* @param message - Message that triggered the command.
* @param args - Evaluated arguments.
*/
- public abstract override exec(message: CommandMessage | SlashMessage, args: any): any;
+ public abstract override exec(message: CommandMessage | SlashMessage, args: CommandArguments): any;
}
type SlashOptionKeys =
diff --git a/lib/extensions/discord-akairo/BotCommandHandler.ts b/lib/extensions/discord-akairo/BotCommandHandler.ts
index 8a4fe60..71d9ad4 100644
--- a/lib/extensions/discord-akairo/BotCommandHandler.ts
+++ b/lib/extensions/discord-akairo/BotCommandHandler.ts
@@ -1,6 +1,7 @@
-import { type BotCommand, type CommandMessage, type SlashMessage } from '#lib';
-import { CommandHandler, type Category, type CommandHandlerEvents, type CommandHandlerOptions } from 'discord-akairo';
-import { type Collection, type Message, type PermissionsString } from 'discord.js';
+import type { BotCommand, CommandMessage, SlashMessage } from '#lib';
+import { CommandHandler, CommandHandlerEvents, type Category, type CommandHandlerOptions } from 'discord-akairo';
+import { GuildMember, PermissionResolvable, type Collection, type Message, type PermissionsString } from 'discord.js';
+import { CommandHandlerEvent } from '../../utils/Constants.js';
export type CustomCommandHandlerOptions = CommandHandlerOptions;
@@ -18,20 +19,116 @@ export interface BotCommandHandlerEvents extends CommandHandlerEvents {
load: [command: BotCommand, isReload: boolean];
messageBlocked: [message: /* no util */ Message | CommandMessage | SlashMessage, reason: string];
messageInvalid: [message: CommandMessage];
- missingPermissions: [message: CommandMessage, command: BotCommand, type: 'client' | 'user', missing: PermissionsString[]];
+ missingPermissions: [
+ message: CommandMessage,
+ command: BotCommand,
+ type: 'client' | 'user',
+ // fix: this is jank
+ missing: (PermissionsString | '[[UnsupportedChannel]]')[]
+ ];
remove: [command: BotCommand];
slashBlocked: [message: SlashMessage, command: BotCommand, reason: string];
slashError: [error: Error, message: SlashMessage, command: BotCommand];
slashFinished: [message: SlashMessage, command: BotCommand, args: any, returnValue: any];
- slashMissingPermissions: [message: SlashMessage, command: BotCommand, type: 'client' | 'user', missing: PermissionsString[]];
+ slashMissingPermissions: [
+ message: SlashMessage,
+ command: BotCommand,
+ type: 'client' | 'user',
+ // fix: this is jank
+ missing: (PermissionsString | '[[UnsupportedChannel]]')[]
+ ];
slashStarted: [message: SlashMessage, command: BotCommand, args: any];
}
export class BotCommandHandler extends CommandHandler {
public declare modules: Collection<string, BotCommand>;
public declare categories: Collection<string, Category<string, BotCommand>>;
+
+ //! this is a simplified version of the original
+ public override async runPermissionChecks(
+ message: Message | SlashMessage,
+ command: BotCommand,
+ slash: boolean = false
+ ): Promise<boolean> {
+ const event = slash ? CommandHandlerEvent.SlashMissingPermissions : CommandHandlerEvent.MissingPermissions;
+
+ const appSlashPerms = slash ? (message as SlashMessage).interaction.appPermissions : null;
+ const userSlashPerms = slash ? (message as SlashMessage).interaction.memberPermissions : null;
+
+ console.dir(message);
+ console.dir(appSlashPerms);
+ console.dir(userSlashPerms);
+ console.dir(event);
+ console.dir(command);
+
+ const noPerms = message.channel == null || (message.channel.isThread() && message.channel.parent == null);
+
+ if (message.inGuild()) {
+ if (noPerms && command.clientCheckChannel && appSlashPerms == null) {
+ this.emit(event, message, command, 'client', ['[[UnsupportedChannel]]']);
+ return true;
+ }
+ if (message.channel?.isDMBased()) return false;
+
+ const missing = command.clientCheckChannel
+ ? (appSlashPerms ?? message.channel?.permissionsFor(message.guild.members.me!))?.missing(command.clientPermissions)
+ : message.guild?.members.me?.permissions.missing(command.clientPermissions);
+
+ if (missing?.length) {
+ this.emit(event, message, command, 'client', missing);
+ return true;
+ }
+ }
+
+ if (command.userPermissions) {
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ const ignorer = command.ignorePermissions || this.ignorePermissions;
+ const isIgnored = Array.isArray(ignorer)
+ ? ignorer.includes(message.author.id)
+ : typeof ignorer === 'function'
+ ? ignorer(message, command)
+ : message.author.id === ignorer;
+
+ if (!isIgnored) {
+ if (message.inGuild()) {
+ if (noPerms && command.userCheckChannel && userSlashPerms == null) {
+ this.emit(event, message, command, 'user', ['[[UnsupportedChannel]]']);
+ return true;
+ }
+ if (message.channel?.isDMBased()) return false;
+
+ const missing = command.userCheckChannel
+ ? (userSlashPerms ?? message.channel?.permissionsFor(message.author))?.missing(command.userPermissions)
+ : message.member?.permissions.missing(command.userPermissions);
+
+ if (missing?.length) {
+ this.emit(event, message, command, 'user', missing);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
}
export interface BotCommandHandler extends CommandHandler {
findCommand(name: string): BotCommand;
}
+
+export function permissionCheck(
+ message: CommandMessage | SlashMessage,
+ check: GuildMember,
+ perms: PermissionResolvable,
+ useChannel: boolean
+): boolean {
+ if (message.inGuild()) {
+ if (!message.channel || message.channel.isDMBased()) return true;
+
+ const missing = useChannel ? message.channel.permissionsFor(check)?.missing(perms) : check.permissions.missing(perms);
+
+ if (missing?.length) return false;
+ }
+ return true;
+}
diff --git a/lib/extensions/discord-akairo/BotInhibitor.ts b/lib/extensions/discord-akairo/BotInhibitor.ts
index d134eab..8892b8b 100644
--- a/lib/extensions/discord-akairo/BotInhibitor.ts
+++ b/lib/extensions/discord-akairo/BotInhibitor.ts
@@ -1,8 +1,12 @@
-import { type BotCommand, type CommandMessage, type SlashMessage } from '#lib';
-import { Inhibitor } from 'discord-akairo';
+import type { BotCommand, CommandMessage, InhibitorReason, InhibitorType, SlashMessage } from '#lib';
+import { Inhibitor, InhibitorOptions } from 'discord-akairo';
import { Message } from 'discord.js';
export abstract class BotInhibitor extends Inhibitor {
+ public constructor(id: InhibitorReason, options?: BotInhibitorOptions) {
+ super(id, options);
+ }
+
/**
* Checks if message should be blocked.
* A return value of true will block the message.
@@ -16,3 +20,28 @@ export abstract class BotInhibitor extends Inhibitor {
public abstract override exec(message: CommandMessage, command: BotCommand): any;
public abstract override exec(message: CommandMessage | SlashMessage, command: BotCommand): any;
}
+
+/**
+ * Options to use for inhibitor execution behavior.
+ */
+export interface BotInhibitorOptions extends InhibitorOptions {
+ /**
+ * Reason emitted when command or message is blocked.
+ * @default ""
+ */
+ reason: InhibitorReason;
+
+ /**
+ * - {@link InhibitorType.All} run on all messages
+ * - {@link InhibitorType.Pre} run on messages not blocked by the built-in inhibitors
+ * - {@link InhibitorType.Post} run on messages that are commands
+ */
+ type: InhibitorType;
+
+ /**
+ * Priority for the inhibitor for when more than one inhibitors block a message.
+ * The inhibitor with the highest priority is the one that is used for the block reason.
+ * @default 0
+ */
+ priority?: number;
+}
diff --git a/lib/extensions/discord-akairo/BotListener.ts b/lib/extensions/discord-akairo/BotListener.ts
index f4bfd6c..4f760e2 100644
--- a/lib/extensions/discord-akairo/BotListener.ts
+++ b/lib/extensions/discord-akairo/BotListener.ts
@@ -1,3 +1,49 @@
-import { Listener } from 'discord-akairo';
+import { Listener, type ListenerOptions } from 'discord-akairo';
-export abstract class BotListener extends Listener {}
+export abstract class BotListener extends Listener {
+ public constructor(id: string, options: BotListenerOptions) {
+ super(id, options);
+ }
+}
+
+/**
+ * Options to use for listener execution behavior.
+ */
+export interface BotListenerOptions extends ListenerOptions {
+ /**
+ * The event emitter, either a key from `ListenerHandler#emitters` or an EventEmitter.
+ */
+ emitter: Emitter;
+
+ /**
+ * Event name to listen to.
+ */
+ event: string;
+
+ /**
+ * Type of listener, either 'on' or 'once'.
+ * @default "on"
+ */
+ type?: ListenerType;
+}
+
+export const enum Emitter {
+ Client = 'client',
+ CommandHandler = 'commandHandler',
+ InhibitorHandler = 'inhibitorHandler',
+ ListenerHandler = 'listenerHandler',
+ TaskHandler = 'taskHandler',
+ ContextMenuCommandHandler = 'contextMenuCommandHandler',
+ Process = 'process',
+ Stdin = 'stdin',
+ Gateway = 'gateway',
+ Rest = 'rest',
+ Ws = 'ws'
+}
+
+export const enum ListenerType {
+ On = 'on',
+ Once = 'once',
+ PrependListener = 'prependListener',
+ PrependOnceListener = 'prependOnceListener'
+}
diff --git a/lib/extensions/discord-akairo/BotListenerHandler.ts b/lib/extensions/discord-akairo/BotListenerHandler.ts
index 9b3b525..bc14a53 100644
--- a/lib/extensions/discord-akairo/BotListenerHandler.ts
+++ b/lib/extensions/discord-akairo/BotListenerHandler.ts
@@ -1,3 +1,19 @@
import { ListenerHandler } from 'discord-akairo';
+import type readline from 'readline';
+import { TanzaniteClient } from './TanzaniteClient.js';
export class BotListenerHandler extends ListenerHandler {}
+
+export interface Emitters {
+ client: TanzaniteClient;
+ commandHandler: TanzaniteClient['commandHandler'];
+ inhibitorHandler: TanzaniteClient['inhibitorHandler'];
+ listenerHandler: TanzaniteClient['listenerHandler'];
+ taskHandler: TanzaniteClient['taskHandler'];
+ contextMenuCommandHandler: TanzaniteClient['contextMenuCommandHandler'];
+ process: NodeJS.Process;
+ stdin: readline.Interface;
+ gateway: TanzaniteClient['ws'];
+ rest: TanzaniteClient['rest'];
+ ws: TanzaniteClient['ws'];
+}
diff --git a/lib/extensions/discord-akairo/TanzaniteClient.ts b/lib/extensions/discord-akairo/TanzaniteClient.ts
index fe34b58..ac09aea 100644
--- a/lib/extensions/discord-akairo/TanzaniteClient.ts
+++ b/lib/extensions/discord-akairo/TanzaniteClient.ts
@@ -11,7 +11,6 @@ import {
snowflake
} from '#args';
import type { Config } from '#config';
-import { BotClientEvents, emojis, formatError, inspect, updateEveryCache } from '#lib';
import { patch, type PatchedElements } from '@notenoughupdates/events-intercept';
import * as Sentry from '@sentry/node';
import {
@@ -62,14 +61,18 @@ import {
} from '../../models/index.js';
import { AllowedMentions } from '../../utils/AllowedMentions.js';
import { BotClientUtils } from '../../utils/BotClientUtils.js';
+import { emojis } from '../../utils/Constants.js';
import { Logger } from '../../utils/Logger.js';
+import { updateEveryCache } from '../../utils/UpdateCache.js';
+import { formatError, inspect } from '../../utils/Utils.js';
+import { BotClientEvents } from '../discord.js/BotClientEvents.js';
import { ExtendedGuild } from '../discord.js/ExtendedGuild.js';
import { ExtendedGuildMember } from '../discord.js/ExtendedGuildMember.js';
import { ExtendedMessage } from '../discord.js/ExtendedMessage.js';
import { ExtendedUser } from '../discord.js/ExtendedUser.js';
import { BotCommandHandler } from './BotCommandHandler.js';
import { BotInhibitorHandler } from './BotInhibitorHandler.js';
-import { BotListenerHandler } from './BotListenerHandler.js';
+import { BotListenerHandler, Emitters } from './BotListenerHandler.js';
import { BotTaskHandler } from './BotTaskHandler.js';
declare module 'discord.js' {
@@ -578,17 +581,3 @@ export interface BotStats {
*/
slashCommandsUsed: bigint;
}
-
-export interface Emitters {
- client: TanzaniteClient;
- commandHandler: TanzaniteClient['commandHandler'];
- inhibitorHandler: TanzaniteClient['inhibitorHandler'];
- listenerHandler: TanzaniteClient['listenerHandler'];
- taskHandler: TanzaniteClient['taskHandler'];
- contextMenuCommandHandler: TanzaniteClient['contextMenuCommandHandler'];
- process: NodeJS.Process;
- stdin: readline.Interface;
- gateway: TanzaniteClient['ws'];
- rest: TanzaniteClient['rest'];
- ws: TanzaniteClient['ws'];
-}
diff --git a/lib/extensions/discord.js/BotClientEvents.ts b/lib/extensions/discord.js/BotClientEvents.ts
index 284ea32..941a6d8 100644
--- a/lib/extensions/discord.js/BotClientEvents.ts
+++ b/lib/extensions/discord.js/BotClientEvents.ts
@@ -1,4 +1,3 @@
-import type { BanResponse, CommandMessage, Guild as GuildDB, GuildSettings } from '#lib';
import type { AkairoClientEvents } from 'discord-akairo';
import type {
ButtonInteraction,
@@ -13,32 +12,10 @@ import type {
Snowflake,
User
} from 'discord.js';
-
-export enum TanzaniteEvent {
- Ban = 'customBan',
- Block = 'customBlock',
- Kick = 'customKick',
- Mute = 'customMute',
- PunishRoleAdd = 'punishRoleAdd',
- PunishRoleRemove = 'punishRoleRemove',
- Purge = 'customPurge',
- RemoveTimeout = 'customRemoveTimeout',
- Timeout = 'customTimeout',
- Unban = 'customUnban',
- Unblock = 'customUnblock',
- Unmute = 'customUnmute',
- UpdateModlog = 'updateModlog',
- UpdateSettings = 'updateSettings',
- Warn = 'customWarn',
- LevelUpdate = 'levelUpdate',
- Lockdown = 'lockdown',
- Unlockdown = 'unlockdown',
- MassBan = 'massBan',
- MassEvidence = 'massEvidence',
- Button = 'button',
- SelectMenu = 'selectMenu',
- ModalSubmit = 'modal'
-}
+import { Guild as GuildDB, GuildSettings } from '../../models/index.js';
+import { TanzaniteEvent } from '../../utils/Constants.js';
+import { CommandMessage } from '../discord-akairo/BotCommand.js';
+import { BanResponse } from './ExtendedGuildMember.js';
export interface BotClientEvents extends AkairoClientEvents {
[TanzaniteEvent.Ban]: [
diff --git a/lib/extensions/discord.js/ExtendedGuild.ts b/lib/extensions/discord.js/ExtendedGuild.ts
index 67de5cf..6b69206 100644
--- a/lib/extensions/discord.js/ExtendedGuild.ts
+++ b/lib/extensions/discord.js/ExtendedGuild.ts
@@ -1,19 +1,7 @@
-import {
- AllowedMentions,
- banResponse,
- colors,
- dmResponse,
- emojis,
- permissionsResponse,
- punishmentEntryRemove,
- TanzaniteClient,
- type BanResponse,
- type GuildFeatures,
- type GuildLogType,
- type GuildModel
-} from '#lib';
import * as Moderation from '#lib/common/Moderation.js';
-import { Guild as GuildDB, ModLogType } from '#lib/models/index.js';
+import { Guild as GuildDB, GuildFeatures, GuildLogType, GuildModel, ModLogType } from '#lib/models/index.js';
+import { AllowedMentions } from '#lib/utils/AllowedMentions.js';
+import { colors, emojis, TanzaniteEvent } from '#lib/utils/Constants.js';
import { addOrRemoveFromArray } from '#lib/utils/Utils.js';
import assert from 'assert/strict';
import {
@@ -43,7 +31,8 @@ import {
type WebhookMessageOptions
} from 'discord.js';
import _ from 'lodash';
-import { TanzaniteEvent } from './BotClientEvents.js';
+import { TanzaniteClient } from '../discord-akairo/TanzaniteClient.js';
+import { banResponse, BanResponse, dmResponse, permissionsResponse, punishmentEntryRemove } from './ExtendedGuildMember.js';
declare module 'discord.js' {
export interface BaseGuild {
diff --git a/lib/extensions/discord.js/ExtendedGuildMember.ts b/lib/extensions/discord.js/ExtendedGuildMember.ts
index 172f6df..043cc1d 100644
--- a/lib/extensions/discord.js/ExtendedGuildMember.ts
+++ b/lib/extensions/discord.js/ExtendedGuildMember.ts
@@ -1,15 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
- formatError,
- Moderation,
- ModLogType,
- TanzaniteClient,
- Time,
- type BotClientEvents,
- type PunishmentTypeDM,
- type ValueOf
-} from '#lib';
-import {
ChannelType,
GuildMember,
PermissionFlagsBits,
@@ -17,7 +7,18 @@ import {
type GuildTextBasedChannel,
type Role
} from 'discord.js';
-import { TanzaniteEvent } from './BotClientEvents.js';
+import {
+ checkMutePermissions,
+ createModLogEntry,
+ createPunishmentEntry,
+ punishDM,
+ PunishmentTypeDM,
+ removePunishmentEntry
+} from '../../common/Moderation.js';
+import { ModLogType } from '../../models/index.js';
+import { TanzaniteEvent, Time } from '../../utils/Constants.js';
+import { formatError, ValueOf } from '../../utils/Utils.js';
+import { TanzaniteClient } from '../discord-akairo/TanzaniteClient.js';
/* eslint-enable @typescript-eslint/no-unused-vars */
declare module 'discord.js' {
@@ -140,7 +141,7 @@ export class ExtendedGuildMember extends GuildMember {
modlog?: string,
sendFooter = true
): Promise<boolean> {
- return Moderation.punishDM({
+ return punishDM({
client: this.client,
modlog,
guild: this.guild,
@@ -166,7 +167,7 @@ export class ExtendedGuildMember extends GuildMember {
const ret = await (async (): Promise<{ result: WarnResponse; caseNum: number | null }> => {
// add modlog entry
- const result = await Moderation.createModLogEntry(
+ const result = await createModLogEntry(
{
client: this.client,
type: ModLogType.WARN,
@@ -214,7 +215,7 @@ export class ExtendedGuildMember extends GuildMember {
const ret = await (async () => {
if (options.addToModlog || options.duration) {
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: options.duration ? ModLogType.TEMP_PUNISHMENT_ROLE : ModLogType.PERM_PUNISHMENT_ROLE,
guild: this.guild,
@@ -230,7 +231,7 @@ export class ExtendedGuildMember extends GuildMember {
caseID = modlog.id;
if (options.addToModlog || options.duration) {
- const punishmentEntrySuccess = await Moderation.createPunishmentEntry({
+ const punishmentEntrySuccess = await createPunishmentEntry({
client: this.client,
type: 'role',
user: this,
@@ -287,7 +288,7 @@ export class ExtendedGuildMember extends GuildMember {
const ret = await (async () => {
if (options.addToModlog) {
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.REMOVE_PUNISHMENT_ROLE,
guild: this.guild,
@@ -301,7 +302,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!modlog) return removeRoleResponse.MODLOG_ERROR;
caseID = modlog.id;
- const punishmentEntrySuccess = await Moderation.removePunishmentEntry({
+ const punishmentEntrySuccess = await removePunishmentEntry({
client: this.client,
type: 'role',
user: this,
@@ -370,7 +371,7 @@ export class ExtendedGuildMember extends GuildMember {
*/
public override async customMute(options: CustomTimedPunishmentOptions): Promise<MuteResponse> {
// checks
- const checks = await Moderation.checkMutePermissions(this.guild);
+ const checks = await checkMutePermissions(this.guild);
if (checks !== true) return checks;
const muteRoleID = (await this.guild.getSetting('muteRole'))!;
@@ -393,7 +394,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!muteSuccess) return muteResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: options.duration ? ModLogType.TEMP_MUTE : ModLogType.PERM_MUTE,
user: this,
@@ -409,7 +410,7 @@ export class ExtendedGuildMember extends GuildMember {
caseID = modlog.id;
// add punishment entry so they can be unmuted later
- const punishmentEntrySuccess = await Moderation.createPunishmentEntry({
+ const punishmentEntrySuccess = await createPunishmentEntry({
client: this.client,
type: 'mute',
user: this,
@@ -456,7 +457,7 @@ export class ExtendedGuildMember extends GuildMember {
*/
public override async customUnmute(options: CustomPunishmentOptions): Promise<UnmuteResponse> {
// checks
- const checks = await Moderation.checkMutePermissions(this.guild);
+ const checks = await checkMutePermissions(this.guild);
if (checks !== true) return checks;
const muteRoleID = (await this.guild.getSetting('muteRole'))!;
@@ -478,7 +479,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!muteSuccess) return unmuteResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.UNMUTE,
user: this,
@@ -493,7 +494,7 @@ export class ExtendedGuildMember extends GuildMember {
caseID = modlog.id;
// remove mute entry
- const removePunishmentEntrySuccess = await Moderation.removePunishmentEntry({
+ const removePunishmentEntrySuccess = await removePunishmentEntry({
client: this.client,
type: 'mute',
user: this,
@@ -548,7 +549,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!moderator) return kickResponse.CANNOT_RESOLVE_USER;
const ret = await (async () => {
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.KICK,
user: this,
@@ -613,7 +614,7 @@ export class ExtendedGuildMember extends GuildMember {
const ret = await (async () => {
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN,
user: this,
@@ -641,7 +642,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!banSuccess) return banResponse.ACTION_ERROR;
// add punishment entry so they can be unbanned later
- const punishmentEntrySuccess = await Moderation.createPunishmentEntry({
+ const punishmentEntrySuccess = await createPunishmentEntry({
client: this.client,
type: 'ban',
user: this,
@@ -699,7 +700,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!blockSuccess) return blockResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: options.duration ? ModLogType.TEMP_CHANNEL_BLOCK : ModLogType.PERM_CHANNEL_BLOCK,
user: this,
@@ -713,7 +714,7 @@ export class ExtendedGuildMember extends GuildMember {
caseID = modlog.id;
// add punishment entry so they can be unblocked later
- const punishmentEntrySuccess = await Moderation.createPunishmentEntry({
+ const punishmentEntrySuccess = await createPunishmentEntry({
client: this.client,
type: 'block',
user: this,
@@ -727,7 +728,7 @@ export class ExtendedGuildMember extends GuildMember {
// dm user
const dmSuccess = options.silent
? null
- : await Moderation.punishDM({
+ : await punishDM({
client: this.client,
punishment: 'blocked',
reason: options.reason ?? undefined,
@@ -793,7 +794,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!blockSuccess) return unblockResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.CHANNEL_UNBLOCK,
user: this,
@@ -807,7 +808,7 @@ export class ExtendedGuildMember extends GuildMember {
caseID = modlog.id;
// remove punishment entry
- const punishmentEntrySuccess = await Moderation.removePunishmentEntry({
+ const punishmentEntrySuccess = await removePunishmentEntry({
client: this.client,
type: 'block',
user: this,
@@ -819,7 +820,7 @@ export class ExtendedGuildMember extends GuildMember {
// dm user
const dmSuccess = options.silent
? null
- : await Moderation.punishDM({
+ : await punishDM({
client: this.client,
punishment: 'unblocked',
reason: options.reason ?? undefined,
@@ -880,7 +881,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!timeoutSuccess) return timeoutResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.TIMEOUT,
user: this,
@@ -942,7 +943,7 @@ export class ExtendedGuildMember extends GuildMember {
if (!timeoutSuccess) return removeTimeoutResponse.ACTION_ERROR;
// add modlog entry
- const { log: modlog } = await Moderation.createModLogEntry({
+ const { log: modlog } = await createModLogEntry({
client: this.client,
type: ModLogType.REMOVE_TIMEOUT,
user: this,
diff --git a/lib/utils/Arg.ts b/lib/utils/Arg.ts
index 99060fb..80ca878 100644
--- a/lib/utils/Arg.ts
+++ b/lib/utils/Arg.ts
@@ -1,10 +1,4 @@
-import {
- type BaseBotArgumentType,
- type BotArgumentType,
- type BotArgumentTypeCaster,
- type CommandMessage,
- type SlashMessage
-} from '#lib';
+import type { BaseBotArgumentType, BotArgumentType, BotArgumentTypeCaster, CommandMessage, SlashMessage } from '#lib';
import { Argument, type Command, type Flag, type ParsedValuePredicate } from 'discord-akairo';
import { type Message } from 'discord.js';
diff --git a/lib/utils/BotClientUtils.ts b/lib/utils/BotClientUtils.ts
index 1dd46bf..4b2c99b 100644
--- a/lib/utils/BotClientUtils.ts
+++ b/lib/utils/BotClientUtils.ts
@@ -1,8 +1,8 @@
-import { ConfigChannelKey } from '#config';
+import type { ConfigChannelKey } from '#config';
import type { CodeBlockLang, CustomInspectOptions } from '#lib';
-import { GlobalCache, SharedCache } from '#lib/common/BotCache.js';
-import { CommandMessage } from '#lib/extensions/discord-akairo/BotCommand.js';
-import { SlashMessage } from '#lib/extensions/discord-akairo/SlashMessage.js';
+import type { GlobalCache, SharedCache } from '#lib/common/BotCache.js';
+import type { CommandMessage } from '#lib/extensions/discord-akairo/BotCommand.js';
+import type { SlashMessage } from '#lib/extensions/discord-akairo/SlashMessage.js';
import { Global, Shared } from '#lib/models/index.js';
import assert from 'assert/strict';
import {
diff --git a/lib/utils/Constants.ts b/lib/utils/Constants.ts
index 8e4871b..c30d01d 100644
--- a/lib/utils/Constants.ts
+++ b/lib/utils/Constants.ts
@@ -1,10 +1,4 @@
import { default as deepLock } from 'deep-lock';
-import {
- ArgumentMatches as AkairoArgumentMatches,
- ArgumentTypes as AkairoArgumentTypes,
- BuiltInReasons,
- CommandHandlerEvents as AkairoCommandHandlerEvents
-} from 'discord-akairo/dist/src/util/Constants.js';
import { Colors, GuildFeature, Snowflake } from 'discord.js';
const rawCapeUrl = 'https://raw.githubusercontent.com/NotEnoughUpdates/capes/master/';
@@ -489,39 +483,6 @@ export const bots: Record<Snowflake, { applicationId: Snowflake }> = {
}
};
-export const ArgumentMatches = Object.freeze({
- ...AkairoArgumentMatches
-} as const);
-
-export const ArgumentTypes = Object.freeze({
- ...AkairoArgumentTypes,
- DURATION: 'duration',
- CONTENT_WITH_DURATION: 'contentWithDuration',
- PERMISSION: 'permission',
- SNOWFLAKE: 'snowflake',
- DISCORD_EMOJI: 'discordEmoji',
- ROLE_WITH_DURATION: 'roleWithDuration',
- ABBREVIATED_NUMBER: 'abbreviatedNumber',
- GLOBAL_USER: 'globalUser'
-} as const);
-
-export const BlockedReasons = Object.freeze({
- ...BuiltInReasons,
- DISABLED_GUILD: 'disabledGuild',
- DISABLED_GLOBAL: 'disabledGlobal',
- ROLE_BLACKLIST: 'roleBlacklist',
- USER_GUILD_BLACKLIST: 'userGuildBlacklist',
- USER_GLOBAL_BLACKLIST: 'userGlobalBlacklist',
- RESTRICTED_GUILD: 'restrictedGuild',
- CHANNEL_GUILD_BLACKLIST: 'channelGuildBlacklist',
- CHANNEL_GLOBAL_BLACKLIST: 'channelGlobalBlacklist',
- RESTRICTED_CHANNEL: 'restrictedChannel'
-} as const);
-
-export const CommandHandlerEvents = Object.freeze({
- ...AkairoCommandHandlerEvents
-} as const);
-
export const moulberryBushRoleMap = deepLock([
{ name: '*', id: '792453550768390194' },
{ name: 'Admin Perms', id: '746541309853958186' },
@@ -552,3 +513,184 @@ export const moulberryBushRoleMap = deepLock([
export type PronounCode = keyof typeof pronounMapping;
export type Pronoun = typeof pronounMapping[PronounCode];
+
+export const enum ArgumentMatches {
+ Phrase = 'phrase',
+ Flag = 'flag',
+ Option = 'option',
+ Rest = 'rest',
+ Separate = 'separate',
+ Text = 'text',
+ Content = 'content',
+ RestContent = 'restContent',
+ None = 'none'
+}
+
+export const enum ArgumentTypes {
+ String = 'string',
+ Lowercase = 'lowercase',
+ Uppercase = 'uppercase',
+ CharCodes = 'charCodes',
+ Number = 'number',
+ Integer = 'integer',
+ Bigint = 'bigint',
+ Emojint = 'emojint',
+ Url = 'url',
+ Date = 'date',
+ Color = 'color',
+ User = 'user',
+ Users = 'users',
+ Member = 'member',
+ Members = 'members',
+ Relevant = 'relevant',
+ Relevants = 'relevants',
+ Channel = 'channel',
+ Channels = 'channels',
+ TextChannel = 'textChannel',
+ TextChannels = 'textChannels',
+ VoiceChannel = 'voiceChannel',
+ VoiceChannels = 'voiceChannels',
+ CategoryChannel = 'categoryChannel',
+ CategoryChannels = 'categoryChannels',
+ NewsChannel = 'newsChannel',
+ NewsChannels = 'newsChannels',
+ StageChannel = 'stageChannel',
+ StageChannels = 'stageChannels',
+ ThreadChannel = 'threadChannel',
+ ThreadChannels = 'threadChannels',
+ DirectoryChannel = 'directoryChannel',
+ DirectoryChannels = 'directoryChannels',
+ ForumChannel = 'forumChannel',
+ ForumChannels = 'forumChannels',
+ TextBasedChannel = 'textBasedChannel',
+ TextBasedChannels = 'textBasedChannels',
+ VoiceBasedChannel = 'voiceBasedChannel',
+ VoiceBasedChannels = 'voiceBasedChannels',
+ Role = 'role',
+ Roles = 'roles',
+ Emoji = 'emoji',
+ Emojis = 'emojis',
+ Guild = 'guild',
+ Guilds = 'guilds',
+ Message = 'message',
+ GuildMessage = 'guildMessage',
+ RelevantMessage = 'relevantMessage',
+ Invite = 'invite',
+ UserMention = 'userMention',
+ MemberMention = 'memberMention',
+ ChannelMention = 'channelMention',
+ RoleMention = 'roleMention',
+ EmojiMention = 'emojiMention',
+ CommandAlias = 'commandAlias',
+ Command = 'command',
+ Inhibitor = 'inhibitor',
+ Listener = 'listener',
+ Task = 'task',
+ ContextMenuCommand = 'contextMenuCommand',
+ Duration = 'duration',
+ contentWithDuration = 'contentWithDuration',
+ Permission = 'permission',
+ DiscordEmoji = 'discordEmoji',
+ RoleWithDuration = 'roleWithDuration',
+ AbbreviatedNumber = 'abbreviatedNumber',
+ GlobalUser = 'globalUser'
+}
+
+export const enum InhibitorReason {
+ Client = 'client',
+ Bot = 'bot',
+ Owner = 'owner',
+ SuperUser = 'superUser',
+ Guild = 'guild',
+ Dm = 'dm',
+ AuthorNotFound = 'authorNotFound',
+ NotNsfw = 'notNsfw',
+ DisabledGuild = 'disabledGuild',
+ DisabledGlobal = 'disabledGlobal',
+ RoleBlacklist = 'roleBlacklist',
+ UserGuildBlacklist = 'userGuildBlacklist',
+ UserGlobalBlacklist = 'userGlobalBlacklist',
+ RestrictedGuild = 'restrictedGuild',
+ ChannelGuildBlacklist = 'channelGuildBlacklist',
+ ChannelGlobalBlacklist = 'channelGlobalBlacklist',
+ RestrictedChannel = 'restrictedChannel',
+ GuildBlacklist = 'guildBlacklist',
+ Fatal = 'fatal',
+ CannotSend = 'cannotSend',
+ GuildUnavailable = 'guildUnavailable'
+}
+
+export const enum InhibitorType {
+ /**
+ * Run on all messages
+ */
+ All = 'all',
+
+ /**
+ * Run on messages not blocked by the built-in inhibitors
+ */
+ Pre = 'pre',
+
+ /**
+ * Run on messages that are commands
+ */
+ Post = 'post'
+}
+
+export const enum CommandHandlerEvent {
+ CommandBlocked = 'commandBlocked',
+ CommandBreakout = 'commandBreakout',
+ CommandCancelled = 'commandCancelled',
+ CommandTimeout = 'commandTimeout',
+ CommandFinished = 'commandFinished',
+ CommandInvalid = 'commandInvalid',
+ CommandLocked = 'commandLocked',
+ CommandStarted = 'commandStarted',
+ Cooldown = 'cooldown',
+ Error = 'error',
+ InPrompt = 'inPrompt',
+ MessageBlocked = 'messageBlocked',
+ MessageInvalid = 'messageInvalid',
+ MissingPermissions = 'missingPermissions',
+ SlashBlocked = 'slashBlocked',
+ SlashError = 'slashError',
+ SlashFinished = 'slashFinished',
+ SlashMissingPermissions = 'slashMissingPermissions',
+ SlashNotFound = 'slashNotFound',
+ SlashStarted = 'slashStarted',
+ SlashOnly = 'slashOnly'
+}
+
+export const enum ContextCommandHandlerEvent {
+ Error = 'error',
+ Finished = 'finished',
+ NotFound = 'notFound',
+ Started = 'started',
+ Blocked = 'blocked'
+}
+
+export const enum TanzaniteEvent {
+ Ban = 'customBan',
+ Block = 'customBlock',
+ Kick = 'customKick',
+ Mute = 'customMute',
+ PunishRoleAdd = 'punishRoleAdd',
+ PunishRoleRemove = 'punishRoleRemove',
+ Purge = 'customPurge',
+ RemoveTimeout = 'customRemoveTimeout',
+ Timeout = 'customTimeout',
+ Unban = 'customUnban',
+ Unblock = 'customUnblock',
+ Unmute = 'customUnmute',
+ UpdateModlog = 'updateModlog',
+ UpdateSettings = 'updateSettings',
+ Warn = 'customWarn',
+ LevelUpdate = 'levelUpdate',
+ Lockdown = 'lockdown',
+ Unlockdown = 'unlockdown',
+ MassBan = 'massBan',
+ MassEvidence = 'massEvidence',
+ Button = 'button',
+ SelectMenu = 'selectMenu',
+ ModalSubmit = 'modal'
+}
diff --git a/lib/utils/Format.ts b/lib/utils/Format.ts
index debaf4b..d1c0a51 100644
--- a/lib/utils/Format.ts
+++ b/lib/utils/Format.ts
@@ -1,4 +1,4 @@
-import { type CodeBlockLang } from '#lib';
+import type { CodeBlockLang } from '#lib';
import {
bold as discordBold,
codeBlock as discordCodeBlock,
diff --git a/lib/utils/Utils.ts b/lib/utils/Utils.ts
index f7404e1..9280e05 100644
--- a/lib/utils/Utils.ts
+++ b/lib/utils/Utils.ts
@@ -1,14 +1,3 @@
-import {
- Arg,
- CommandMessage,
- SlashEditMessageType,
- SlashSendMessageType,
- TanzaniteClient,
- timeUnits,
- type BaseBotArgumentType,
- type CustomInspectOptions,
- type SlashMessage
-} from '#lib';
import { humanizeDuration as humanizeDurationMod } from '@notenoughupdates/humanize-duration';
import assert from 'assert/strict';
import cp from 'child_process';
@@ -21,14 +10,21 @@ import {
OAuth2Scopes,
PermissionFlagsBits,
PermissionsBitField,
+ PermissionsString,
type APIEmbed,
type APIMessage,
type CommandInteraction,
- type InteractionReplyOptions,
- type PermissionsString
+ type InteractionReplyOptions
} from 'discord.js';
import { DeepWritable } from 'ts-essentials';
import { inspect as inspectUtil, promisify } from 'util';
+import { BaseBotArgumentType, CommandMessage } from '../extensions/discord-akairo/BotCommand.js';
+import { SlashMessage } from '../extensions/discord-akairo/SlashMessage.js';
+import { TanzaniteClient } from '../extensions/discord-akairo/TanzaniteClient.js';
+import { CustomInspectOptions } from '../types/InspectOptions.js';
+import { SlashEditMessageType, SlashSendMessageType } from '../types/misc.js';
+import * as Arg from './Arg.js';
+import { mappings, timeUnits } from './Constants.js';
import * as Format from './Format.js';
export type StripPrivate<T> = { [K in keyof T]: T[K] extends Record<string, any> ? StripPrivate<T[K]> : T[K] };
@@ -428,68 +424,8 @@ export function getSymbols(obj: Record<string, any>): symbol[] {
return symbols;
}
-/**
- * Checks if a user has a certain guild permission (doesn't check channel permissions).
- * @param message The message to check the user from.
- * @param permissions The permissions to check for.
- * @returns The missing permissions or null if none are missing.
- */
-export function userGuildPermCheck(
- message: CommandMessage | SlashMessage,
- permissions: typeof PermissionFlagsBits[keyof typeof PermissionFlagsBits][]
-): PermissionsString[] | null {
- if (!message.inGuild()) return null;
- const missing = message.member?.permissions.missing(permissions) ?? [];
-
- return missing.length ? missing : null;
-}
-
-/**
- * Check if the client has certain permissions in the guild (doesn't check channel permissions).
- * @param message The message to check the client user from.
- * @param permissions The permissions to check for.
- * @returns The missing permissions or null if none are missing.
- */
-export function clientGuildPermCheck(message: CommandMessage | SlashMessage, permissions: bigint[]): PermissionsString[] | null {
- const missing = message.guild?.members.me?.permissions.missing(permissions) ?? [];
-
- return missing.length ? missing : null;
-}
-
-/**
- * Check if the client has permission to send messages in the channel as well as check if they have other permissions
- * in the guild (or the channel if `checkChannel` is `true`).
- * @param message The message to check the client user from.
- * @param permissions The permissions to check for.
- * @param checkChannel Whether to check the channel permissions instead of the guild permissions.
- * @returns The missing permissions or null if none are missing.
- */
-export function clientSendAndPermCheck(
- message: CommandMessage | SlashMessage,
- permissions: bigint[] = [],
- checkChannel = false
-): PermissionsString[] | null {
- if (!message.inGuild() || !message.channel) return null;
-
- const missing: PermissionsString[] = [];
- const sendPerm = message.channel.isThread() ? 'SendMessages' : 'SendMessagesInThreads';
-
- // todo: remove once forum channels are fixed
- if (message.channel.parent === null && message.channel.isThread()) return null;
-
- if (!message.guild.members.me!.permissionsIn(message.channel!.id).has(sendPerm)) missing.push(sendPerm);
-
- missing.push(
- ...(checkChannel
- ? message.guild!.members.me!.permissionsIn(message.channel!.id!).missing(permissions)
- : clientGuildPermCheck(message, permissions) ?? [])
- );
-
- return missing.length ? missing : null;
-}
-
+export * as arg from './Arg.js';
export { deepLock as deepFreeze };
-export { Arg as arg };
export { Format as format };
export { DiscordConstants as discordConstants };
export { AkairoUtil as akairo };
@@ -613,3 +549,7 @@ export function formatError(error: Error | any, colors = false): string {
export function deepWriteable<T>(obj: T): DeepWritable<T> {
return obj as DeepWritable<T>;
}
+
+export function formatPerms(permissions: PermissionsString[]) {
+ return permissions.map((p) => `\`${mappings.permissions[p]?.name ?? p}\``).join(', ');
+}