Skip to content

Commit 802b8fa

Browse files
authored
feat: support for command permissions v2 (#432)
feat: support for command permissions v2
2 parents c9d6940 + 5ed20e4 commit 802b8fa

File tree

5 files changed

+89
-48
lines changed

5 files changed

+89
-48
lines changed

src/handlers/MessageCommandHandler.ts

+19
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import { CommandContext } from '../lib/structures/contexts/CommandContext';
2626
import { Util } from '../lib/util/Util';
2727
import { Logger, Events } from '../lib/util/logger/Logger';
28+
import { MemberPermissions } from '../inhibitors';
2829

2930
const cooldowns = new Collection<string, Collection<string, number>>();
3031

@@ -152,6 +153,7 @@ export async function MessageCommandHandler(
152153
}
153154

154155
if (!command.type.includes(CommandType.MESSAGE)) return;
156+
if (!message.guild && command.dmPermission === false) return;
155157

156158
if (command.cooldown) {
157159
const cooldown = Handlers.cooldownHandler(
@@ -238,6 +240,23 @@ export async function MessageCommandHandler(
238240
},
239241
});
240242

243+
if (
244+
command.defaultMemberPermissions &&
245+
!command.inhibitors?.some(
246+
inhibitor => inhibitor.constructor.name !== 'MemberPermissions',
247+
)
248+
) {
249+
if (
250+
!Util.runInhibitor(
251+
ctx,
252+
new MemberPermissions({
253+
permissions: [command.defaultMemberPermissions],
254+
}),
255+
)
256+
)
257+
return;
258+
}
259+
241260
if (!(await command.inhibit(ctx))) return;
242261
await Promise.resolve(command.run(ctx))
243262
.catch(async error => {

src/lib/structures/Command.ts

+26-24
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Commands } from '../managers/CommandManager';
66
import { Locale, LocaleString } from '../util/common';
77
import { Logger } from '../util/logger/Logger';
88
import { commandAndOptionNameRegexp } from '../util/regexes';
9+
import { PermissionResolvable, Permissions } from 'discord.js';
10+
import { Util } from '../util/Util';
911

1012
export enum CommandType {
1113
/**
@@ -26,17 +28,19 @@ export enum CommandType {
2628
'CONTEXT_MESSAGE' = 3,
2729
}
2830

29-
export type CommandInhibitor = (ctx: CommandContext) => boolean | any;
30-
export type CommandInhibitors = Array<
31-
{ run: CommandInhibitor } | CommandInhibitor
32-
>;
31+
export type CommandInhibitor =
32+
| ((ctx: CommandContext) => boolean | any)
33+
| { run: CommandInhibitor };
34+
export type CommandInhibitors = Array<CommandInhibitor>;
3335

3436
export interface CommandOptions {
3537
name: string;
3638
nameLocalizations?: Record<LocaleString, string>;
3739
description?: string;
3840
descriptionLocalizations?: Record<LocaleString, string>;
3941
type: Array<CommandType | keyof typeof CommandType>;
42+
defaultMemberPermissions?: PermissionResolvable;
43+
dmPermission?: boolean;
4044
arguments?: Array<Argument | ArgumentOptions>;
4145
inhibitors?: CommandInhibitors;
4246
guildId?: string;
@@ -84,8 +88,10 @@ const validationSchema = z
8488
)
8589
.array()
8690
.nonempty(),
91+
defaultMemberPermissions: z.any().optional(),
92+
dmPermission: z.boolean().optional(),
8793
arguments: z.any().array().optional(),
88-
inhibitors: z.any().array().optional(),
94+
inhibitors: z.any().array().optional().default([]),
8995
guildId: z.string().optional(),
9096
cooldown: z.string().optional(),
9197
autoDefer: z
@@ -109,6 +115,8 @@ export class Command {
109115
public description?: string;
110116
public descriptionLocalizations?: Record<LocaleString, string>;
111117
public type: Array<CommandType | keyof typeof CommandType>;
118+
public defaultMemberPermissions?: PermissionResolvable;
119+
public dmPermission?: boolean;
112120
public arguments?: Array<Argument>;
113121
public inhibitors: CommandInhibitors;
114122
public guildId?: string;
@@ -136,6 +144,11 @@ export class Command {
136144
options.descriptionLocalizations ||
137145
Command.defaults?.descriptionLocalizations;
138146
this.type = options.type || Command.defaults?.type;
147+
this.defaultMemberPermissions =
148+
options.defaultMemberPermissions ||
149+
Command.defaults?.defaultMemberPermissions;
150+
this.dmPermission =
151+
options.dmPermission || Command.defaults?.dmPermission;
139152
this.arguments = options.arguments?.map(argument => {
140153
if (argument instanceof Argument) return argument;
141154
else return new Argument(argument);
@@ -174,26 +187,9 @@ export class Command {
174187
if (!this.inhibitors) return true;
175188

176189
for await (const inhibitor of this.inhibitors) {
177-
let result;
178-
if (typeof inhibitor === 'function') {
179-
result = await Promise.resolve(inhibitor(ctx)).catch(error => {
180-
Logger.error(
181-
typeof error.code !== 'undefined' ? error.code : '',
182-
error.message,
183-
);
184-
if (error.stack) Logger.trace(error.stack);
185-
});
186-
} else if (typeof inhibitor.run === 'function') {
187-
result = await Promise.resolve(inhibitor.run(ctx)).catch(error => {
188-
Logger.error(
189-
typeof error.code !== 'undefined' ? error.code : '',
190-
error.message,
191-
);
192-
if (error.stack) Logger.trace(error.stack);
193-
});
194-
}
195-
if (result !== true) return false;
190+
if ((await Util.runInhibitor(ctx, inhibitor)) !== true) return false;
196191
}
192+
197193
return true;
198194
}
199195

@@ -218,6 +214,12 @@ export class Command {
218214
name_localizations: this.nameLocalizations,
219215
description: this.description,
220216
description_localizations: this.descriptionLocalizations,
217+
dm_permission: this.dmPermission,
218+
default_member_permissions: this.defaultMemberPermissions
219+
? new Permissions(
220+
this.defaultMemberPermissions,
221+
).bitfield.toString()
222+
: null,
221223
options: this.arguments?.map(argument => argument.toJSON()),
222224
type: type,
223225
};

src/lib/structures/Component.ts

+7-23
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import type { ComponentContext } from './contexts/ComponentContext';
33
import { AutoDeferType, GClient } from '../GClient';
44
import { Components } from '../managers/ComponentManager';
55
import { Logger } from '../util/logger/Logger';
6+
import { Util } from '../util/Util';
67

78
export enum ComponentType {
89
'BUTTON' = 1,
910
'SELECT_MENU' = 2,
1011
}
1112

12-
export type ComponentInhibitor = (ctx: ComponentContext) => boolean | any;
13-
export type ComponentInhibitors = Array<
14-
{ run: ComponentInhibitor } | ComponentInhibitor
15-
>;
13+
export type ComponentInhibitor =
14+
| ((ctx: ComponentContext) => boolean | any)
15+
| { run: ComponentInhibitor };
16+
export type ComponentInhibitors = Array<ComponentInhibitor>;
1617

1718
export interface ComponentOptions {
1819
name: string;
@@ -116,26 +117,9 @@ export class Component {
116117
if (!this.inhibitors) return true;
117118

118119
for await (const inhibitor of this.inhibitors) {
119-
let result;
120-
if (typeof inhibitor === 'function') {
121-
result = await Promise.resolve(inhibitor(ctx)).catch(error => {
122-
Logger.error(
123-
typeof error.code !== 'undefined' ? error.code : '',
124-
error.message,
125-
);
126-
if (error.stack) Logger.trace(error.stack);
127-
});
128-
} else if (typeof inhibitor.run === 'function') {
129-
result = await Promise.resolve(inhibitor.run(ctx)).catch(error => {
130-
Logger.error(
131-
typeof error.code !== 'undefined' ? error.code : '',
132-
error.message,
133-
);
134-
if (error.stack) Logger.trace(error.stack);
135-
});
136-
}
137-
if (result !== true) return false;
120+
if ((await Util.runInhibitor(ctx, inhibitor)) !== true) return false;
138121
}
122+
139123
return true;
140124
}
141125

src/lib/structures/contexts/CommandContext.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ export class CommandContext<
104104
| MessagePayload
105105
| InteractionReplyOptions,
106106
): Promise<Fetch extends true ? GuildCacheMessage<Cached> : void> {
107-
return this.deferred || this.replied || this.interaction?.deferred || this.interaction?.replied
107+
return this.deferred ||
108+
this.replied ||
109+
this.interaction?.deferred ||
110+
this.interaction?.replied
108111
? this.editReply(options)
109112
: this.reply(options);
110113
}

src/lib/util/Util.ts

+33
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import type { Client } from 'discord.js';
22
import { Logger } from './logger/Logger';
33
import type { GClient } from '../GClient';
44
import { Plugins } from '../managers/PluginManager';
5+
import type { CommandContext } from '../structures/contexts/CommandContext';
6+
import type { ComponentContext } from '../structures/contexts/ComponentContext';
7+
import type { CommandInhibitor } from '../structures/Command';
8+
import type { ComponentInhibitor } from '../structures/Component';
59

610
export class Util {
711
/**
@@ -128,6 +132,35 @@ export class Util {
128132
.replace(new RegExp(/\w/), s => s.toUpperCase());
129133
}
130134

135+
static async runInhibitor(
136+
ctx: CommandContext | ComponentContext,
137+
inhibitor: CommandInhibitor | ComponentInhibitor,
138+
) {
139+
let result;
140+
if (typeof inhibitor === 'function') {
141+
// @ts-expect-error Duplication
142+
result = await Promise.resolve(inhibitor(ctx)).catch(error => {
143+
Logger.error(
144+
typeof error.code !== 'undefined' ? error.code : '',
145+
error.message,
146+
);
147+
if (error.stack) Logger.trace(error.stack);
148+
});
149+
} else if (typeof inhibitor.run === 'function') {
150+
// @ts-expect-error Duplication
151+
result = await Promise.resolve(inhibitor.run(ctx)).catch(error => {
152+
Logger.error(
153+
typeof error.code !== 'undefined' ? error.code : '',
154+
error.message,
155+
);
156+
if (error.stack) Logger.trace(error.stack);
157+
});
158+
}
159+
160+
if (result !== true) return false;
161+
return true;
162+
}
163+
131164
static async getResponse(
132165
value: string,
133166
interaction: { client: Client | GClient },

0 commit comments

Comments
 (0)