最後活躍 1723994097

quick.rs 原始檔案
1use serde::{Deserialize, Serialize};
2use std::collections::HashSet;
3
4/// The base permissions for quick lockdown
5static BASE_PERMS: [serenity::model::permissions::Permissions; 3] = [
6 serenity::all::Permissions::VIEW_CHANNEL,
7 serenity::all::Permissions::SEND_MESSAGES,
8 serenity::all::Permissions::SEND_MESSAGES_IN_THREADS,
9];
10
11/// The result of a `test_quick_lockdown` call
12#[derive(Debug, Serialize, Deserialize, Clone)]
13pub struct QuickLockdownTestResult {
14 /// Which roles need to be changed/fixed combined with the permissions they are missing
15 pub changes_needed:
16 std::collections::HashMap<serenity::all::RoleId, serenity::all::Permissions>,
17 /// The critical roles (either member roles or the `@everyone` role)
18 pub critical_roles: HashSet<serenity::all::RoleId>,
19}
20
21/// Returns the critical roles given a [PartialGuild](`serenity::all::PartialGuild`) and a set of member roles
22pub fn get_critical_roles(
23 pg: &serenity::all::PartialGuild,
24 member_roles: HashSet<serenity::all::RoleId>,
25) -> Result<HashSet<serenity::all::RoleId>, silverpelt::Error> {
26 if member_roles.is_empty() {
27 // Find the everyone role
28 let everyone_role = pg
29 .roles
30 .iter()
31 .find(|r| r.id.get() == pg.id.get())
32 .ok_or_else(|| silverpelt::Error::from("No @everyone role found"))?;
33
34 Ok(std::iter::once(everyone_role.id).collect())
35 } else {
36 Ok(member_roles)
37 }
38}
39
40/// Given a [PartialGuild](`serenity::all::PartialGuild`) and a set of member roles, `test_quick_lockdown` will check if the guild meets the requirements for quick lockdown.
41///
42/// The requirements for quick lockdown are listed in README.md and the basic idea is listed below:
43///
44/// - One can define a set of critical roles which are either the member roles or the ``@everyone`` role, all other roles must not have View Channel, Send Messages and/or Send Messages In Threads permissions
45pub async fn test_quick_lockdown(
46 pg: &serenity::all::PartialGuild,
47 member_roles: HashSet<serenity::all::RoleId>,
48) -> Result<QuickLockdownTestResult, silverpelt::Error> {
49 let critical_roles = get_critical_roles(pg, member_roles)?;
50
51 let mut changes_needed = std::collections::HashMap::new();
52
53 // From here on out, we only need to care about critical and non critical roles
54 for role in pg.roles.iter() {
55 if critical_roles.contains(&role.id) {
56 let mut needed_perms = serenity::all::Permissions::empty();
57
58 let mut missing = false;
59 for perm in BASE_PERMS {
60 if !role.permissions.contains(perm) {
61 needed_perms |= perm;
62 missing = true;
63 }
64 }
65
66 if missing {
67 changes_needed.insert(role.id, needed_perms);
68 }
69 }
70 }
71
72 Ok(QuickLockdownTestResult {
73 changes_needed,
74 critical_roles,
75 })
76}
77
78/// Creates a new quick lockdown given a [PartialGuild](`serenity::all::PartialGuild`) and a set of critical roles
79///
80/// This is achieved by **removing** the `BASE_PERMS` from the critical roles
81pub async fn create_quick_lockdown(
82 ctx: &serenity::client::Context,
83 pg: &mut serenity::all::PartialGuild,
84 critical_roles: HashSet<serenity::all::RoleId>,
85) -> Result<(), silverpelt::Error> {
86 let mut new_roles = Vec::new();
87 for role in pg.roles.iter() {
88 if critical_roles.contains(&role.id) {
89 let mut perms = role.permissions;
90
91 for perm in BASE_PERMS {
92 perms.remove(perm);
93 }
94
95 new_roles.push(
96 pg.id
97 .edit_role(
98 &ctx.http,
99 role.id,
100 serenity::all::EditRole::new().permissions(perms),
101 )
102 .await?,
103 );
104 }
105 }
106
107 for role in new_roles {
108 pg.roles.insert(role);
109 }
110
111 Ok(())
112}
113
114/// Reverts a quick lockdown given a [PartialGuild](`serenity::all::PartialGuild`) and a set of critical roles
115///
116/// This is achieved by **adding** the `BASE_PERMS` to the critical roles
117pub async fn revert_quick_lockdown(
118 ctx: &serenity::client::Context,
119 pg: &mut serenity::all::PartialGuild,
120 critical_roles: HashSet<serenity::all::RoleId>,
121) -> Result<(), silverpelt::Error> {
122 let mut new_roles = Vec::new();
123 for role in pg.roles.iter() {
124 if critical_roles.contains(&role.id) {
125 let mut perms = role.permissions;
126
127 let mut changed = false;
128 for perm in BASE_PERMS {
129 if !perms.contains(perm) {
130 changed = true;
131 perms |= perm;
132 }
133 }
134
135 if !changed {
136 continue; // Avoid useless API call when no changes are needed
137 }
138
139 new_roles.push(
140 pg.id
141 .edit_role(
142 &ctx.http,
143 role.id,
144 serenity::all::EditRole::new().permissions(perms),
145 )
146 .await?,
147 );
148 }
149 }
150
151 for role in new_roles {
152 pg.roles.insert(role);
153 }
154
155 Ok(())
156}
157