1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
use chrono::Utc;
use crate::core::colours;
use crate::core::consts::*;
use crate::core::consts::DB as db;
use crate::core::model::TC;
use crate::core::utils::*;
use serenity::framework::standard::{
Args,
Command,
CommandError,
CommandOptions
};
use serenity::model::channel::Message;
use serenity::model::id::{
ChannelId,
RoleId
};
use serenity::model::guild::Member;
use serenity::model::Permissions;
use serenity::prelude::Context;
use std::sync::Arc;
pub struct Register;
impl Command for Register {
fn options(&self) -> Arc<CommandOptions> {
let default = CommandOptions::default();
let options = CommandOptions {
desc: Some("A premium command that adds roles to a user (from the self roles list only), and depending on the settings for the command, will apply either a member role or a cooldown role with a timer. When the timer ends, cooldown is removed and member is added. In order for the switch to occur automatically, this command must be used. See the premium commands for more information on configuring this command.".to_string()),
usage: Some("<user_resolvable> <role_resolvables as CSV>".to_string()),
example: Some("@fun gamer, techie".to_string()),
aliases: vec!["reg"].iter().map(|e| e.to_string()).collect(),
required_permissions: Permissions::MANAGE_ROLES,
..default
};
Arc::new(options)
}
fn execute(&self, ctx: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> {
debug!("REGISTER TRACE: Begin register for user: {}", message.author.id.0);
if let Some(guild_id) = message.guild_id {
let settings = db.get_premium(guild_id.0 as i64).map_err(|_| "Premium is required to use this command.")?;
let guild_data = db.get_guild(guild_id.0 as i64)?;
let roles = db.get_roles(guild_id.0 as i64)?;
debug!("REGISTER TRACE: Settings obtained");
match parse_user(args.single::<String>().unwrap_or(String::new()), guild_id) {
Some((user_id, mut member)) => {
debug!("REGISTER TRACE: User matched");
let channel = if guild_data.modlog {
ChannelId(guild_data.modlog_channel as u64)
} else { message.channel_id };
let list = args.rest().split(",").map(|s| s.trim().to_string());
let mut to_add = Vec::new();
for r1 in list {
if let Some((r, _)) = parse_role(r1.clone(), guild_id) {
if settings.cooldown_restricted_roles.contains(&(r.0 as i64)) { continue; }
to_add.push(r);
} else if let Some(i) = roles.iter().position(|r| r.aliases.contains(&r1)) {
if settings.cooldown_restricted_roles.contains(&(roles[i].id)) { continue; }
to_add.push(RoleId(roles[i].id as u64));
}
}
debug!("REGISTER TRACE: Resolved roles");
let mut to_add = filter_roles(to_add, guild_id.member(&message.author)?);
debug!("REGISTER TRACE: Filtered roles");
for (i, role_id) in to_add.clone().iter().enumerate() {
if member.roles.contains(role_id) {
to_add.remove(i);
continue;
}
if let Err(_) = member.add_role(*role_id) {
to_add.remove(i);
};
}
debug!("REGISTER TRACE: Roles added");
if let Some(role) = settings.register_cooldown_role {
member.add_role(RoleId(role as u64))?;
debug!("REGISTER TRACE: Added cooldown role");
if let Some(member_role) = settings.register_member_role {
debug!("REGISTER TRACE: Added member role");
let data = ctx.data.lock();
let tc_lock = data.get::<TC>().ok_or("Failed to obtain timer client.")?;
let tc = tc_lock.lock();
let dur = match settings.register_cooldown_duration {
Some(dur) => dur,
None => DAY as i32,
};
let data = format!("COOLDOWN||{}||{}||{}||{}",
user_id.0,
guild_id.0,
member_role,
role);
let start_time = Utc::now().timestamp();
let end_time = start_time + dur as i64;
check_error!(db.new_timer(start_time, end_time, data));
tc.request();
debug!("REGISTER TRACE: Timer registered");
}
} else if let Some(role) = settings.register_member_role {
member.add_role(RoleId(role as u64))?;
debug!("REGISTER TRACE: Added member role (No cooldown role)");
}
let desc = if !to_add.is_empty() {
to_add.iter().map(|r| match r.to_role_cached() {
Some(role) => role.name,
None => r.0.to_string(),
})
.collect::<Vec<String>>()
.join("\n")
} else { String::new() };
debug!("REGISTER TRACE: Built log message");
channel.send_message(|m| m
.embed(|e| e
.title(format!(
"Registered {} with the following roles:",
member.user.read().tag()
))
.description(desc)
.colour(member.colour().unwrap_or(*colours::MAIN))
.timestamp(now!())
))?;
debug!("REGISTER TRACE: Sent log message");
if guild_data.introduction && guild_data.introduction_channel>0 {
let channel = ChannelId(guild_data.introduction_channel as u64);
if guild_data.introduction_type == "embed" {
send_welcome_embed(guild_data.introduction_message, &member, channel)?;
} else {
channel.say(parse_welcome_items(guild_data.introduction_message, &member))?;
}
debug!("REGISTER TRACE: Sent introduction message");
}
},
None => { message.channel_id.say("I couldn't find that user.")?; }
}
} else { failed!(GUILDID_FAIL); }
debug!("REGISTER TRACE: Register completed successfully");
Ok(())
}
}
pub struct AddRole;
impl Command for AddRole {
fn options(&self) -> Arc<CommandOptions> {
let default = CommandOptions::default();
let options = CommandOptions {
desc: Some("Welcome to the club! Add a role(s) to someone!".to_string()),
usage: Some("<user_resolvable> <role_resolvables as CSV>".to_string()),
example: Some("@fun red, green".to_string()),
aliases: vec!["ar"].iter().map(|e| e.to_string()).collect(),
required_permissions: Permissions::MANAGE_ROLES,
..default
};
Arc::new(options)
}
fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> {
if let Some(guild_id) = message.guild_id {
if let Some((_, mut member)) = parse_user(args.single::<String>()?, guild_id) {
let list = args.rest().split(",").map(|s| s.trim().to_string());
let mut to_add = Vec::new();
let mut failed = Vec::new();
for r1 in list {
if let Some((s,_)) = parse_role(r1.clone(), guild_id) {
to_add.push(s);
} else {
failed.push(format!("Could not locate {}", r1));
}
}
let mut to_add = filter_roles(to_add, guild_id.member(&message.author)?);
for (i, role_id) in to_add.clone().iter().enumerate() {
if member.roles.contains(role_id) {
to_add.remove(i);
failed.push(format!(
"You already have {}",
match role_id.to_role_cached() {
Some(role) => role.name,
None => role_id.0.to_string(),
}));
}
if let Err(_) = member.add_role(*role_id) {
to_add.remove(i);
failed.push(format!(
"Failed to add {}",
match role_id.to_role_cached() {
Some(role) => role.name,
None => role_id.0.to_string(),
}));
};
}
let mut fields = Vec::new();
if !to_add.is_empty() {
fields.push(("Added Roles", to_add.iter()
.map(|r| match r.to_role_cached() {
Some(role) => role.name,
None => r.0.to_string(),
})
.collect::<Vec<String>>()
.join("\n"),
false));
}
if !failed.is_empty() {
fields.push(("Failed to Add", failed.join("\n"), false));
}
message.channel_id.send_message(|m| m
.embed(|e| e
.title("Add Role Summary")
.fields(fields)
.colour(member.colour().unwrap_or(*colours::MAIN))
))?;
}
} else { failed!(GUILDID_FAIL); }
Ok(())
}
}
pub struct RemoveRole;
impl Command for RemoveRole {
fn options(&self) -> Arc<CommandOptions> {
let default = CommandOptions::default();
let options = CommandOptions {
desc: Some("It doesn't have to end like this... Remove someone's role(s).".to_string()),
usage: Some("<user_resolvable> <role_resolvables as CSV>".to_string()),
example: Some("@fun red, green".to_string()),
aliases: vec!["rr"].iter().map(|e| e.to_string()).collect(),
required_permissions: Permissions::MANAGE_ROLES,
..default
};
Arc::new(options)
}
fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> {
if let Some(guild_id) = message.guild_id {
if let Some((_, mut member)) = parse_user(args.single::<String>()?, guild_id) {
let list = args.rest().split(",").map(|s| s.trim().to_string());
let mut to_remove = Vec::new();
let mut failed = Vec::new();
for r1 in list {
if let Some((s,_)) = parse_role(r1.clone(), guild_id) {
to_remove.push(s);
} else {
failed.push(format!("Could not locate {}", r1));
}
}
let mut to_remove = filter_roles(to_remove, guild_id.member(&message.author)?);
for (i, role_id) in to_remove.clone().iter().enumerate() {
if !member.roles.contains(role_id) {
to_remove.remove(i);
failed.push(format!(
"You don't have {}",
match role_id.to_role_cached() {
Some(role) => role.name,
None => role_id.0.to_string(),
}));
}
if let Err(_) = member.remove_role(*role_id) {
to_remove.remove(i);
failed.push(format!(
"Failed to remove {}",
match role_id.to_role_cached() {
Some(role) => role.name,
None => role_id.0.to_string(),
}));
};
}
let mut fields = Vec::new();
if !to_remove.is_empty() {
fields.push(("Removed Roles", to_remove.iter()
.map(|r| match r.to_role_cached() {
Some(role) => role.name,
None => r.0.to_string(),
})
.collect::<Vec<String>>()
.join("\n"),
false));
}
if !failed.is_empty() {
fields.push(("Failed to Remove", failed.join("\n"), false));
}
message.channel_id.send_message(|m| m
.embed(|e| e
.title("Remove Role Summary")
.fields(fields)
.colour(member.colour().unwrap_or(*colours::MAIN))
))?;
}
} else { failed!(GUILDID_FAIL); }
Ok(())
}
}
pub struct RoleColour;
impl Command for RoleColour {
fn options(&self) -> Arc<CommandOptions> {
let default = CommandOptions::default();
let options = CommandOptions {
desc: Some("Feeling \"colourful\"? Change the colour of a role!".to_string()),
usage: Some("<role_resolvable> <colour>".to_string()),
example: Some("418130449089691658 e0ffff".to_string()),
aliases: vec!["rc", "rolecolor"].iter().map(|e| e.to_string()).collect(),
required_permissions: Permissions::MANAGE_ROLES,
..default
};
Arc::new(options)
}
fn execute(&self, _: &mut Context, message: &Message, mut args: Args) -> Result<(), CommandError> {
if let Some(guild_id) = message.guild_id {
match parse_role(args.single_quoted::<String>().unwrap_or(String::new()), guild_id) {
Some((_, mut role)) => {
let input = args.single::<String>()?;
let colour_as_hex = if input.starts_with("#") {
&input[1..]
} else { input.as_str() };
let colour = u64::from_str_radix(colour_as_hex, 16)?;
role.edit(|r| r.colour(colour))?;
message.channel_id.say(format!("Colour of `{}` changed to `#{:06X}`", role.name, colour))?;
},
None => { message.channel_id.say("I couldn't find that role")?; },
}
} else { failed!(GUILDID_FAIL); }
Ok(())
}
}
fn filter_roles(roles: Vec<RoleId>, member: Member) -> Vec<RoleId> {
let highest = match member.highest_role_info() {
Some((_,h)) => h,
None => -1,
};
roles.into_iter()
.filter_map(|r| {
let role = r.to_role_cached()?;
match role.position >= highest {
true => None,
false => Some(r),
}
})
.collect()
}
|