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

Contents of /branches/6.x/src/services/PermissionManager.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 7195 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
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 { PermissionOverwrite } from "@prisma/client";
21 import { log } from "console";
22 import { GuildMember, PermissionFlagsBits, PermissionResolvable, PermissionsBitField, Role, Snowflake } from "discord.js";
23 import Service from "../core/Service";
24 import AbstractPermissionManager from "../utils/AbstractPermissionManager";
25 import DiscordBasedPermissionManager from "../utils/DiscordBasedPermissionManager";
26 import LayerBasedPermissionManager from "../utils/LayerBasedPermissionManager";
27 import LevelBasedPermissionManager from "../utils/LevelBasedPermissionManager";
28 import { logInfo } from "../utils/logger";
29 import { GuildConfig } from "./ConfigManager";
30
31 export const name = "permissionManager";
32
33 export type GetMemberPermissionInGuildResult = {
34 permissions: PermissionsBitField;
35 } & (
36 | {
37 type: "levels";
38 level: number;
39 }
40 | {
41 type: "discord";
42 }
43 | {
44 type: "layered";
45 highestOverwrite?: PermissionOverwrite;
46 highestRoleHavingOverwrite?: Role;
47 overwriteIds: number[];
48 }
49 );
50
51 export default class PermissionManager<M extends AbstractPermissionManager = AbstractPermissionManager> extends Service {
52 protected readonly cache: Record<`${Snowflake}_${Snowflake}`, object> = {};
53 protected readonly managers: Record<NonNullable<GuildConfig["permissions"]["mode"]>, AbstractPermissionManager | undefined> =
54 {
55 layered: new LayerBasedPermissionManager(this.client),
56 discord: new DiscordBasedPermissionManager(this.client),
57 levels: new LevelBasedPermissionManager(this.client)
58 };
59
60 boot() {
61 if (!this.client.configManager.systemConfig.sync_permission_managers_on_boot) {
62 return;
63 }
64
65 for (const managerName in this.managers) {
66 this.managers[managerName as keyof typeof this.managers]?.sync?.();
67 logInfo(`[${this.constructor.name}] Synchronizing ${managerName} permission manager`);
68 }
69 }
70
71 async isImmuneToAutoMod(member: GuildMember, permission?: PermissionResolvable[] | PermissionResolvable) {
72 if (this.client.configManager.systemConfig.system_admins.includes(member.user.id)) return true;
73
74 const config = this.client.configManager.config[member.guild.id];
75
76 if (!config) return true;
77
78 const { admin_role, mod_role, staff_role, check_discord_permissions, invincible_roles } = config.permissions ?? {};
79
80 if (member.roles.cache.hasAny(admin_role ?? "_", mod_role ?? "_", staff_role ?? "_")) {
81 log("Member has roles that are immune to automod");
82 return true;
83 }
84
85 for (const roleId of invincible_roles) {
86 if (member.roles.cache.has(roleId)) {
87 log("Member has roles that are immune to this action");
88 return false;
89 }
90 }
91
92 if (check_discord_permissions === "automod" || check_discord_permissions === "both") {
93 const hasDiscordPerms =
94 member.permissions.has(PermissionFlagsBits.ManageGuild, true) ||
95 (permission && member.permissions.has(permission, true));
96
97 if (hasDiscordPerms) {
98 log("Member has discord permissions that are immune to automod");
99 return true;
100 }
101 }
102
103 const manager = await this.getManager(member.guild.id);
104 const { permissions } = manager.getMemberPermissions(member);
105 const hasPermissions = permissions.has("Administrator") || permissions.has("ManageGuild");
106
107 if (hasPermissions) {
108 log("Member has permissions that are immune to automod");
109 return true;
110 }
111
112 return manager.isImmuneToAutoMod(member, permission);
113 }
114
115 // FIXME: Move these permission checks to DiscordBasedPermissionManager
116 async shouldModerate(member: GuildMember, moderator: GuildMember) {
117 if (this.client.configManager.systemConfig.system_admins.includes(moderator.user.id)) return true;
118
119 const config = this.client.configManager.config[member.guild.id];
120
121 if (!config) return false;
122
123 if (member.guild.ownerId === member.user.id) return false;
124 if (member.guild.ownerId === moderator.user.id) return true;
125
126 const { admin_role, mod_role, staff_role, invincible_roles, check_discord_permissions } = config.permissions ?? {};
127
128 if (member.roles.cache.hasAny(admin_role ?? "_", mod_role ?? "_", staff_role ?? "_")) {
129 log("Member has roles that are immune to this action");
130 return false;
131 }
132
133 for (const roleId of invincible_roles) {
134 if (member.roles.cache.has(roleId)) {
135 log("Member has roles that are immune to this action");
136 return false;
137 }
138 }
139
140 if (
141 (check_discord_permissions === "manual_actions" || check_discord_permissions === "both") &&
142 member.roles.highest.position >= moderator.roles.highest.position
143 ) {
144 log("Member has higher/equal roles than moderator");
145 return false;
146 }
147
148 const manager = await this.getManager(member.guild.id);
149 return await manager.shouldModerate(member, moderator);
150 }
151
152 protected getMode(guildId: string) {
153 return this.client.configManager.config[guildId]?.permissions.mode ?? "discord";
154 }
155
156 async getManager(guildId: string): Promise<M> {
157 const manager = this.managers[this.getMode(guildId)];
158
159 if (!manager) {
160 throw new Error("Unknown/Unsupported permission mode: " + this.getMode(guildId));
161 }
162
163 await manager.triggerSyncIfNeeded();
164 return manager as M;
165 }
166
167 async getMemberPermissions(member: GuildMember, mergeWithDiscordPermissions?: boolean) {
168 console.log("before Getting member permissions");
169 const manager = await this.getManager(member.guild.id);
170 console.log("Getting member permissions");
171 return manager.getMemberPermissions(member, mergeWithDiscordPermissions);
172 }
173
174 usesLayeredMode(guildId: string) {
175 return this.getMode(guildId) === "layered";
176 }
177
178 usesLevelBasedMode(guildId: string): this is PermissionManager<LevelBasedPermissionManager> {
179 return this.getMode(guildId) === "levels";
180 }
181
182 usesDiscordMode(guildId: string) {
183 return this.getMode(guildId) === "discord";
184 }
185 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26