Last active 1723994097

rootspring's Avatar rootspring revised this gist 1723994097. Go to revision

1 file changed, 156 insertions

quick.rs(file created)

@@ -0,0 +1,156 @@
1 + use serde::{Deserialize, Serialize};
2 + use std::collections::HashSet;
3 +
4 + /// The base permissions for quick lockdown
5 + static 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)]
13 + pub 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
22 + pub 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
45 + pub 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
81 + pub 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
117 + pub 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 + }
Newer Older