1 |
/** |
2 |
* This file is part of SudoBot. |
3 |
* |
4 |
* Copyright (C) 2021-2023 OSN Developers. |
5 |
* |
6 |
* SudoBot is free software; you can redistribute it and/or modify it |
7 |
* under the terms of the GNU Affero General Public License as published by |
8 |
* the Free Software Foundation, either version 3 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* SudoBot is distributed in the hope that it will be useful, but |
12 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU Affero General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU Affero General Public License |
17 |
* along with SudoBot. If not, see <https://www.gnu.org/licenses/>. |
18 |
*/ |
19 |
|
20 |
import { CommandPermissionOverwrite } from "@prisma/client"; |
21 |
import { Awaitable, GuildMember, PermissionsString, Snowflake } from "discord.js"; |
22 |
import Service from "../core/Service"; |
23 |
import { GatewayEventListener } from "../decorators/GatewayEventListener"; |
24 |
import { HasEventListeners } from "../types/HasEventListeners"; |
25 |
import { log } from "../utils/Logger"; |
26 |
import { GetMemberPermissionInGuildResult } from "./PermissionManager"; |
27 |
|
28 |
export const name = "commandPermissionOverwriteManager"; |
29 |
|
30 |
export default class CommandPermissionOverwriteManager extends Service implements HasEventListeners { |
31 |
readonly permissionOverwrites = new Map<`${Snowflake}____${string}`, CommandPermissionOverwrite[]>(); |
32 |
readonly validators: Array<keyof this> = [ |
33 |
"validatePermissionOverwriteUsers", |
34 |
"validatePermissionOverwriteChannels", |
35 |
"validatePermissionOverwriteRoles", |
36 |
"validatePermissionOverwriteLevels", |
37 |
"validatePermissionOverwritePermissions" |
38 |
]; |
39 |
|
40 |
@GatewayEventListener("ready") |
41 |
async onReady() { |
42 |
log("Syncing command permission overwrites..."); |
43 |
|
44 |
const permissionOverwrites = await this.client.prisma.commandPermissionOverwrite.findMany(); |
45 |
this.permissionOverwrites.clear(); |
46 |
|
47 |
for (const permissionOverwrite of permissionOverwrites) { |
48 |
for (const command of permissionOverwrite.commands) { |
49 |
const key = `${permissionOverwrite.guildId}____${command}` as const; |
50 |
let existingOverwriteArray = this.permissionOverwrites.get(key); |
51 |
|
52 |
if (!existingOverwriteArray) { |
53 |
existingOverwriteArray = []; |
54 |
this.permissionOverwrites.set(key, existingOverwriteArray); |
55 |
} |
56 |
|
57 |
existingOverwriteArray.push(permissionOverwrite); |
58 |
} |
59 |
} |
60 |
|
61 |
log("Successfully synced command permission overwrites"); |
62 |
} |
63 |
|
64 |
async validatePermissionOverwritePermissions( |
65 |
permissionOverwrite: CommandPermissionOverwrite, |
66 |
_: ValidatePermissionOverwritesOptions, |
67 |
{ permissions }: GetMemberPermissionInGuildResult |
68 |
) { |
69 |
return ( |
70 |
(permissionOverwrite.requiredPermissionMode === "AND" && |
71 |
permissions.has(permissionOverwrite.requiredPermissions as PermissionsString[], true)) || |
72 |
(permissionOverwrite.requiredPermissionMode === "OR" && |
73 |
permissions.any(permissionOverwrite.requiredPermissions as PermissionsString[], true)) |
74 |
); |
75 |
} |
76 |
|
77 |
validatePermissionOverwriteChannels( |
78 |
permissionOverwrite: CommandPermissionOverwrite, |
79 |
{ channelId }: ValidatePermissionOverwritesOptions |
80 |
) { |
81 |
if (permissionOverwrite.requiredChannels.length === 0) { |
82 |
return true; |
83 |
} |
84 |
|
85 |
return permissionOverwrite.requiredChannels.includes(channelId); |
86 |
} |
87 |
|
88 |
validatePermissionOverwriteUsers( |
89 |
permissionOverwrite: CommandPermissionOverwrite, |
90 |
{ member }: ValidatePermissionOverwritesOptions |
91 |
) { |
92 |
if (permissionOverwrite.requiredUsers.length === 0) { |
93 |
return true; |
94 |
} |
95 |
|
96 |
return permissionOverwrite.requiredUsers.includes(member.user.id); |
97 |
} |
98 |
|
99 |
validatePermissionOverwriteRoles( |
100 |
permissionOverwrite: CommandPermissionOverwrite, |
101 |
{ member }: ValidatePermissionOverwritesOptions |
102 |
) { |
103 |
if (permissionOverwrite.requiredRoles.length === 0) { |
104 |
return true; |
105 |
} |
106 |
|
107 |
for (const roleId of member.roles.cache.keys()) { |
108 |
if (permissionOverwrite.requiredRoles.includes(roleId)) { |
109 |
return true; |
110 |
} |
111 |
} |
112 |
|
113 |
return false; |
114 |
} |
115 |
|
116 |
validatePermissionOverwriteLevels( |
117 |
permissionOverwrite: CommandPermissionOverwrite, |
118 |
_: ValidatePermissionOverwritesOptions, |
119 |
permissions: GetMemberPermissionInGuildResult |
120 |
) { |
121 |
if (permissions.type !== "levels" || permissionOverwrite.requiredLevel === null) { |
122 |
return true; |
123 |
} |
124 |
|
125 |
return permissions.level >= permissionOverwrite.requiredLevel; |
126 |
} |
127 |
|
128 |
async validatePermissionOverwrites(options: ValidatePermissionOverwritesOptions) { |
129 |
const { guildId, commandName, member } = options; |
130 |
const permissionOverwrites = this.client.commandPermissionOverwriteManager.permissionOverwrites.get( |
131 |
`${guildId!}____${commandName}` |
132 |
); |
133 |
const hasOverwrite = !!permissionOverwrites?.length; |
134 |
|
135 |
if (!permissionOverwrites?.length) { |
136 |
return { hasOverwrite: false, result: true }; |
137 |
} |
138 |
|
139 |
const memberPermissions = await this.client.permissionManager.getMemberPermissions(member, true); |
140 |
let result = true; |
141 |
|
142 |
outerLoop: for (const permissionOverwrite of permissionOverwrites) { |
143 |
for (const validator of this.validators) { |
144 |
const method = this[validator] as PermissionValidatorFunction | undefined; |
145 |
const validationResult = !!(await method?.call(this, permissionOverwrite, options, memberPermissions)); |
146 |
|
147 |
if (permissionOverwrite.mode === "AND" && !validationResult) { |
148 |
return { hasOverwrite, result: false }; |
149 |
} |
150 |
|
151 |
if (permissionOverwrite.mode === "OR" && validationResult) { |
152 |
result &&= true; |
153 |
continue outerLoop; |
154 |
} |
155 |
} |
156 |
|
157 |
result &&= permissionOverwrite.mode === "AND"; |
158 |
|
159 |
if (!result) { |
160 |
break; |
161 |
} |
162 |
} |
163 |
|
164 |
return { hasOverwrite, result }; |
165 |
} |
166 |
} |
167 |
|
168 |
type ValidatePermissionOverwritesOptions = { |
169 |
guildId: Snowflake; |
170 |
commandName: string; |
171 |
member: GuildMember; |
172 |
channelId: Snowflake; |
173 |
}; |
174 |
|
175 |
type PermissionValidatorFunction = ( |
176 |
permissionOverwrite: CommandPermissionOverwrite, |
177 |
{ member }: ValidatePermissionOverwritesOptions, |
178 |
permissions?: GetMemberPermissionInGuildResult |
179 |
) => Awaitable<boolean>; |