/[sudobot]/branches/5.x/src/services/PermissionManager.ts
ViewVC logotype

Annotation of /branches/5.x/src/services/PermissionManager.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 13690 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 /**
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 { PermissionRole } from "@prisma/client";
21     import { GuildMember, PermissionFlagsBits, PermissionResolvable, 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 { PermissionLevelsRecord, getPermissionLevels } from "../utils/PermissionLevels";
26     import { log, logWarn } from "../utils/logger";
27    
28     export const name = "permissionManager";
29    
30     export default class PermissionManager extends Service implements HasEventListeners {
31     public readonly users: Record<string, Map<string, PermissionRole> | undefined> = {};
32     public readonly roles: Record<string, Map<string, PermissionRole> | undefined> = {};
33     public readonly levels: PermissionLevelsRecord = getPermissionLevels();
34     public readonly guildPermissionLevels: Record<string, number | undefined> = {};
35    
36     @GatewayEventListener("ready")
37     async onReady() {
38     await this.sync();
39     }
40    
41     shouldModerate(member: GuildMember, moderator: GuildMember) {
42     if (this.client.configManager.systemConfig.system_admins.includes(moderator.user.id)) return true;
43    
44     const config = this.client.configManager.config[member.guild.id];
45    
46     if (!config) return false;
47    
48     if (member.guild.ownerId === member.user.id) return false;
49     if (member.guild.ownerId === moderator.user.id) return true;
50    
51     const { admin_role, mod_role, staff_role } = config.permissions ?? {};
52    
53     if (member.roles.cache.hasAny(admin_role ?? "_", mod_role ?? "_", staff_role ?? "_")) {
54     log("Member has roles that are immune to this action");
55     return false;
56     }
57    
58     if (member.roles.highest.position >= moderator.roles.highest.position) {
59     log("Member has higher roles than moderator");
60     return false;
61     }
62    
63     return true;
64     }
65    
66     isImmuneToAutoMod(member: GuildMember, permission?: PermissionResolvable[] | PermissionResolvable) {
67     if (this.client.configManager.systemConfig.system_admins.includes(member.user.id)) return true;
68    
69     const config = this.client.configManager.config[member.guild.id];
70    
71     if (!config) return true;
72    
73     const { admin_role, mod_role, staff_role } = config.permissions ?? {};
74    
75     if (member.roles.cache.hasAny(admin_role ?? "_", mod_role ?? "_", staff_role ?? "_")) {
76     log("Member has roles that are immune to automod");
77     return true;
78     }
79    
80     const hasDiscordPerms =
81     member.permissions.has(PermissionFlagsBits.ManageGuild, true) || (permission && member.permissions.has(permission, true));
82    
83     if (hasDiscordPerms) {
84     log("Member has discord permissions that are immune to automod");
85     return true;
86     }
87    
88     const permissions = this.getMemberPermissions(member, false);
89     const hasPermissions = permissions.has("Administrator") || permissions.has("ManageGuild");
90    
91     if (hasPermissions) log("Member has permissions that are immune to automod");
92    
93     return hasPermissions;
94     }
95    
96     getMemberPermissionFromLevel(member: GuildMember, mergeWithDiscordPermissions?: boolean) {
97     const level = this.getMemberPermissionLevel(member);
98    
99     return new Set<PermissionsString>([...(mergeWithDiscordPermissions ? member.permissions.toArray() : []), ...this.levels[level]]);
100     }
101    
102     getMemberPermissionLevel(member: GuildMember) {
103     let level = this.guildPermissionLevels[`${member.guild.id}_u_${member.user.id}`] ?? 0;
104    
105     for (const [id] of member.roles.cache) {
106     const roleLevel = this.guildPermissionLevels[`${member.guild.id}_r_${id}`] ?? 0;
107    
108     if (roleLevel > level) {
109     level = roleLevel;
110     }
111     }
112    
113     return level < 0 ? 0 : level > 100 ? 100 : level;
114     }
115    
116     getPermissionsFromPermissionRole(permissionRole: PermissionRole, guildId: string) {
117     const guildUsesPermissionLevels = this.client.configManager.config[guildId]?.permissions.mode === "levels";
118    
119     if (guildUsesPermissionLevels && permissionRole.level !== undefined && permissionRole.level !== null) {
120     return this.levels[permissionRole.level];
121     }
122    
123     return permissionRole.grantedPermissions as readonly PermissionsString[];
124     }
125    
126     getPermissionsFromPermissionRoles(permissionRoles: PermissionRole[], guildId: string) {
127     const guildUsesPermissionLevels = this.client.configManager.config[guildId]?.permissions.mode === "levels";
128     const permissionStrings = new Set<PermissionsString>();
129    
130     for (const permissionRole of permissionRoles) {
131     if (guildUsesPermissionLevels && permissionRole.level !== undefined && permissionRole.level !== null) {
132     for (const permission of this.levels[permissionRole.level]) {
133     permissionStrings.add(permission);
134     }
135     } else {
136     for (const permission of permissionRole.grantedPermissions as readonly PermissionsString[]) {
137     permissionStrings.add(permission);
138     }
139     }
140     }
141    
142     return permissionStrings;
143     }
144    
145     getPermissionsFromPermissionRoleSet(permissionRole: PermissionRole, guildId: string) {
146     return new Set(this.getPermissionsFromPermissionRole(permissionRole, guildId));
147     }
148    
149     getMemberPermissions(member: GuildMember, mergeWithDiscordPermissions?: boolean) {
150     const guildUsesPermissionLevels = this.client.configManager.config[member.guild.id]?.permissions.mode === "levels";
151    
152     if (guildUsesPermissionLevels) {
153     return this.getMemberPermissionFromLevel(member, mergeWithDiscordPermissions);
154     }
155    
156     const allPermissions = new Set<PermissionsString>(mergeWithDiscordPermissions ? member.permissions.toArray() : []);
157    
158     if (this.users[member.guild.id]) {
159     const permissions = this.users[`${member.guild.id}_${member.user.id}`];
160    
161     if (permissions) {
162     for (const permission of permissions.values()) {
163     for (const key of permission.grantedPermissions) {
164     allPermissions.add(key as PermissionsString);
165     }
166     }
167     }
168     }
169    
170     for (const [id] of member.roles.cache) {
171     const permissions = this.roles[`${member.guild.id}_${id}`];
172    
173     if (!permissions) {
174     continue;
175     }
176    
177     for (const permission of permissions.values()) {
178     for (const key of permission.grantedPermissions) {
179     allPermissions.add(key as PermissionsString);
180     }
181     }
182     }
183    
184     return allPermissions;
185     }
186    
187     async sync(guildId?: string) {
188     log("Started syncing permission levels", guildId ? `for guild ${guildId}` : "");
189    
190     const permissionRoles = await this.client.prisma.permissionRole.findMany({
191     where: {
192     guild_id: guildId
193     }
194     });
195    
196     for (const permissionRole of permissionRoles) {
197     const guildUsesPermissionLevels = this.client.configManager.config[permissionRole.guild_id]?.permissions.mode === "levels";
198    
199     for (const roleId of permissionRole.roles) {
200     if (guildUsesPermissionLevels && typeof permissionRole.level === "number") {
201     this.guildPermissionLevels[`${permissionRole.guild_id}_r_${roleId}`] = permissionRole.level;
202     continue;
203     }
204    
205     this.roles[`${permissionRole.guild_id}_${roleId}`] ??= new Map();
206     this.roles[`${permissionRole.guild_id}_${roleId}`]!.set(permissionRole.name, permissionRole);
207     }
208    
209     for (const userId of permissionRole.users) {
210     if (guildUsesPermissionLevels && typeof permissionRole.level === "number") {
211     this.guildPermissionLevels[`${permissionRole.guild_id}_u_${userId}`] = permissionRole.level;
212     continue;
213     }
214    
215     this.users[`${permissionRole.guild_id}_${userId}`] ??= new Map();
216     this.users[`${permissionRole.guild_id}_${userId}`]!.set(permissionRole.name, permissionRole);
217     }
218     }
219    
220     log("Completed syncing permission levels", guildId ? `for guild ${guildId}` : "");
221     }
222    
223     async createPermissionLevel({ guildId, name, permissions, roles, users }: CreatePermissionLevelPayload) {
224     const existingPermissionLevel = await this.client.prisma.permissionRole.findFirst({
225     where: {
226     name,
227     guild_id: guildId
228     }
229     });
230    
231     if (existingPermissionLevel) {
232     return null;
233     }
234    
235     const permissionLevel = await this.client.prisma.permissionRole.create({
236     data: {
237     name,
238     guild_id: guildId,
239     grantedPermissions: permissions,
240     roles,
241     users
242     }
243     });
244    
245     for (const roleId of permissionLevel.roles) {
246     this.roles[`${permissionLevel.guild_id}_${roleId}`] ??= new Map();
247     this.roles[`${permissionLevel.guild_id}_${roleId}`]!.set(permissionLevel.name, permissionLevel);
248     }
249    
250     for (const userId of permissionLevel.users) {
251     this.users[`${permissionLevel.guild_id}_${userId}`] ??= new Map();
252     this.users[`${permissionLevel.guild_id}_${userId}`]!.set(permissionLevel.name, permissionLevel);
253     }
254    
255     return permissionLevel;
256     }
257    
258     async deletePermissionLevel({ guildId, name }: Pick<CreatePermissionLevelPayload, "guildId" | "name">) {
259     const permissionLevel = await this.client.prisma.permissionRole.findFirst({
260     where: {
261     name,
262     guild_id: guildId
263     }
264     });
265    
266     if (!permissionLevel) return null;
267    
268     await this.client.prisma.permissionRole.delete({
269     where: {
270     id: permissionLevel.id
271     }
272     });
273    
274     for (const roleId of permissionLevel.roles) {
275     this.roles[`${permissionLevel.guild_id}_${roleId}`] ??= new Map();
276    
277     if (this.roles[`${permissionLevel.guild_id}_${roleId}`]?.has(permissionLevel.name)) {
278     this.roles[`${permissionLevel.guild_id}_${roleId}`]!.delete(permissionLevel.name);
279     }
280     }
281    
282     for (const userId of permissionLevel.users) {
283     this.users[`${permissionLevel.guild_id}_${userId}`] ??= new Map();
284    
285     if (this.users[`${permissionLevel.guild_id}_${userId}`]?.has(permissionLevel.name)) {
286     this.users[`${permissionLevel.guild_id}_${userId}`]!.delete(permissionLevel.name);
287     }
288     }
289    
290     return permissionLevel;
291     }
292    
293     async updatePermissionLevel({ guildId, name, permissions, newName, roles, users }: CreatePermissionLevelPayload & { newName?: string }) {
294     const existingPermissionLevel = await this.client.prisma.permissionRole.findFirst({
295     where: {
296     name,
297     guild_id: guildId
298     }
299     });
300    
301     if (!existingPermissionLevel) {
302     return null;
303     }
304    
305     if (newName !== name || roles || users || permissions) {
306     const permissionLevel = await this.client.prisma.permissionRole.update({
307     where: {
308     id: existingPermissionLevel.id
309     },
310     data: {
311     name: newName,
312     roles,
313     users,
314     grantedPermissions: permissions
315     }
316     });
317    
318     for (const roleId of permissionLevel.roles) {
319     this.roles[`${permissionLevel.guild_id}_${roleId}`] ??= new Map();
320    
321     if (this.roles[`${permissionLevel.guild_id}_${roleId}`]!.has(name)) {
322     this.roles[`${permissionLevel.guild_id}_${roleId}`]!.delete(name);
323     }
324    
325     this.roles[`${permissionLevel.guild_id}_${roleId}`]!.set(permissionLevel.name, permissionLevel);
326     }
327    
328     for (const userId of permissionLevel.users) {
329     this.users[`${permissionLevel.guild_id}_${userId}`] ??= new Map();
330    
331     if (this.users[`${permissionLevel.guild_id}_${userId}`]!.has(name)) {
332     this.users[`${permissionLevel.guild_id}_${userId}`]!.delete(name);
333     }
334    
335     this.users[`${permissionLevel.guild_id}_${userId}`]!.set(permissionLevel.name, permissionLevel);
336     }
337    
338     return permissionLevel;
339     }
340    
341     logWarn("Nothing updated");
342     return false;
343     }
344     }
345    
346     interface CreatePermissionLevelPayload {
347     guildId: Snowflake;
348     name: string;
349     permissions: PermissionsString[];
350     roles?: Snowflake[];
351     users?: Snowflake[];
352     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26