-
Notifications
You must be signed in to change notification settings - Fork 1
/
_utils.ts
138 lines (127 loc) · 3.76 KB
/
_utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import Command from '../Command';
import ExecutionContext from '../ExecutionContext';
import { hasPermission } from '../permissions';
import CommandOptionChoice from '../CommandOptionChoice';
import * as Discord from '../Discord';
import SubCommand from '../SubCommand';
export const guildRoleListToMap = (
roles: Discord.Role[],
): Record<string, Discord.Role> => {
const map: Record<string, Discord.Role> = {};
for (const r of roles) {
map[r.id] = r;
}
return map;
};
export const compareRank = (
guildRoles: Record<string, Discord.Role>,
m1: Discord.GuildMember,
m2: Discord.GuildMember,
): number => {
const m1Rank = getHighestRank(m1.roles.map(id => guildRoles[id]));
const m2Rank = getHighestRank(m2.roles.map(id => guildRoles[id]));
return m1Rank - m2Rank;
};
/**
* Returns the highest rank number out of all the provided roles. Higher ranks
* mean higher precedence.
*
* @param roles An array of Discord roles to check.
*/
export const getHighestRank = (roles: Discord.Role[]): number => {
let highest = 0;
for (const r of roles) {
if (r.position > highest) {
highest = r.position;
}
}
return highest;
};
/**
* Counts the number of subcommands, out of the array of command options,
* that the executor of the execution context can access.
*
* @param ctx The execution context.
* @param cmd The command.
*/
export const countCtxAccessibleSubCommands = (
ctx: ExecutionContext,
cmd: Command,
): number => {
const validSubCommands =
Object.values(cmd.subCommands).filter(subCommand => {
if (!(subCommand instanceof SubCommand)) return false;
return checkCtxPermissions(ctx, subCommand.requiredPerms)[1];
}) || [];
return validSubCommands.length;
};
/**
* Returns whether the executor of the provided execution context can run the
* provided command.
*
* @param ctx The execution context.
* @param cmd The command being run.
*/
export const ctxCanRunCommand = (
ctx: ExecutionContext,
cmd: Command,
): boolean => {
return (
// Ensure the executor possesses the required permissions.
checkCtxPermissions(ctx, cmd.requiredPerms)[1] &&
// Ensure the executor can access at least 1 subcommand;
(countCtxAccessibleSubCommands(ctx, cmd) !== 0 ||
// OR there are no subcommands.
Object.values(cmd.subCommands).length === 0)
);
};
/**
* Converts a list of commands into a list of command option choices.
*
* This is used by the `/help` command to auto-complete the names of commands
* with help texts.
*
* @param commandsList A list of commands.
*/
export const convertCommandListToOptionsList = (
commandsList: Command[],
): CommandOptionChoice[] => {
const choices: CommandOptionChoice[] = [];
for (const command of commandsList) {
choices.push(
new CommandOptionChoice({
name: command.name,
value: command.name,
}),
);
}
return choices;
};
/**
* Checks whether the executor of the provided execution context has all of the
* provided required permissions.
*
* Returns a 2-element array:
* - 0: The missing Discord permission, or undefined.
* - 1: Whether all permission were met.
*
* @param ctx The execution context.
* @param requiredPerms The required permissions.
*/
export const checkCtxPermissions = (
ctx: ExecutionContext,
requiredPerms: Discord.Permission[],
): [Discord.Permission, false] | [undefined, true] => {
const { interaction } = ctx;
if (!interaction.member) {
throw new Error('No member found in interaction');
}
const executorPerms = parseInt(interaction.member.permissions ?? '0');
// Ensure that every required permission is possessed by the executor.
for (const req of requiredPerms) {
if (!hasPermission(executorPerms, req)) {
return [req, false];
}
}
return [undefined, true];
};