Skip to content

Commit 00f8d18

Browse files
authored
Merge pull request #56 from rust-lang/help-menu-improvements
Improved help menu
2 parents c008157 + d951d53 commit 00f8d18

File tree

6 files changed

+283
-92
lines changed

6 files changed

+283
-92
lines changed

src/api.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@ pub(crate) fn slow_mode(args: Args) -> Result<()> {
7575
Ok(())
7676
}
7777

78+
pub(crate) fn slow_mode_help(args: Args) -> Result<()> {
79+
let help_string = "
80+
Set slowmode on a channel
81+
```
82+
?slowmode {channel} {seconds}
83+
```
84+
**Example:**
85+
```
86+
?slowmode #bot-usage 10
87+
```
88+
will set slowmode on the `#bot-usage` channel with a delay of 10 seconds.
89+
90+
**Disable slowmode:**
91+
```
92+
?slowmode #bot-usage 0
93+
```
94+
will disable slowmode on the `#bot-usage` channel.";
95+
send_reply(&args, &help_string)?;
96+
Ok(())
97+
}
98+
7899
/// Kick a user from the guild.
79100
///
80101
/// Requires the kick members permission
@@ -95,3 +116,18 @@ pub(crate) fn kick(args: Args) -> Result<()> {
95116
}
96117
Ok(())
97118
}
119+
120+
pub(crate) fn kick_help(args: Args) -> Result<()> {
121+
let help_string = "
122+
Kick a user from the guild
123+
```
124+
?kick {user}
125+
```
126+
**Example:**
127+
```
128+
?kick @someuser
129+
```
130+
will kick a user from the guild.";
131+
send_reply(&args, &help_string)?;
132+
Ok(())
133+
}

src/ban.rs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -79,43 +79,41 @@ pub(crate) fn start_unban_thread(cx: Context) {
7979
///
8080
/// Requires the ban members permission
8181
pub(crate) fn temp_ban(args: Args) -> Result<()> {
82-
if api::is_mod(&args)? {
83-
let user_id = parse_username(
84-
&args
85-
.params
86-
.get("user")
87-
.ok_or("unable to retrieve user param")?,
88-
)
89-
.ok_or("unable to retrieve user id")?;
82+
let user_id = parse_username(
83+
&args
84+
.params
85+
.get("user")
86+
.ok_or("unable to retrieve user param")?,
87+
)
88+
.ok_or("unable to retrieve user id")?;
9089

91-
use std::str::FromStr;
90+
use std::str::FromStr;
9291

93-
let hours = u64::from_str(
94-
args.params
95-
.get("hours")
96-
.ok_or("unable to retrieve hours param")?,
97-
)?;
92+
let hours = u64::from_str(
93+
args.params
94+
.get("hours")
95+
.ok_or("unable to retrieve hours param")?,
96+
)?;
9897

99-
let reason = args
100-
.params
101-
.get("reason")
102-
.ok_or("unable to retrieve reason param")?;
98+
let reason = args
99+
.params
100+
.get("reason")
101+
.ok_or("unable to retrieve reason param")?;
103102

104-
if let Some(guild) = args.msg.guild(&args.cx) {
105-
info!("Banning user from guild");
106-
let user = UserId::from(user_id);
103+
if let Some(guild) = args.msg.guild(&args.cx) {
104+
info!("Banning user from guild");
105+
let user = UserId::from(user_id);
107106

108-
user.create_dm_channel(args.cx)?
109-
.say(args.cx, ban_message(reason, hours))?;
107+
user.create_dm_channel(args.cx)?
108+
.say(args.cx, ban_message(reason, hours))?;
110109

111-
guild.read().ban(args.cx, &user, &"all")?;
110+
guild.read().ban(args.cx, &user, &"all")?;
112111

113-
save_ban(
114-
format!("{}", user_id),
115-
format!("{}", guild.read().id),
116-
hours,
117-
)?;
118-
}
112+
save_ban(
113+
format!("{}", user_id),
114+
format!("{}", guild.read().id),
115+
hours,
116+
)?;
119117
}
120118
Ok(())
121119
}
@@ -130,7 +128,7 @@ Ban a user for a temporary amount of time
130128
```
131129
{command}
132130
```
133-
Example:
131+
**Example:**
134132
```
135133
?ban @someuser {hours} {reason}
136134
```

src/commands.rs

Lines changed: 118 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
1-
use crate::state_machine::{CharacterSet, StateMachine};
1+
use crate::{
2+
api,
3+
state_machine::{CharacterSet, StateMachine},
4+
};
25
use reqwest::blocking::Client as HttpClient;
36
use serenity::{model::channel::Message, prelude::Context};
47
use std::{collections::HashMap, sync::Arc};
58

69
const PREFIX: &'static str = "?";
710
pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
8-
pub(crate) type CmdPtr = Arc<dyn for<'m> Fn(Args<'m>) -> Result<()> + Send + Sync>;
11+
pub(crate) type GuardFn = fn(&Args) -> Result<bool>;
12+
13+
struct Command {
14+
guard: GuardFn,
15+
ptr: Box<dyn for<'m> Fn(Args<'m>) -> Result<()> + Send + Sync>,
16+
}
17+
18+
impl Command {
19+
fn authorize(&self, args: &Args) -> Result<bool> {
20+
(self.guard)(&args)
21+
}
22+
23+
fn call(&self, args: Args) -> Result<()> {
24+
(self.ptr)(args)
25+
}
26+
}
927

1028
pub struct Args<'m> {
1129
pub http: &'m HttpClient,
@@ -15,24 +33,33 @@ pub struct Args<'m> {
1533
}
1634

1735
pub(crate) struct Commands {
18-
state_machine: StateMachine,
36+
state_machine: StateMachine<Arc<Command>>,
1937
client: HttpClient,
20-
menu: Option<String>,
38+
menu: Option<HashMap<&'static str, (&'static str, GuardFn)>>,
2139
}
2240

2341
impl Commands {
2442
pub(crate) fn new() -> Self {
2543
Self {
2644
state_machine: StateMachine::new(),
2745
client: HttpClient::new(),
28-
menu: Some(String::new()),
46+
menu: Some(HashMap::new()),
2947
}
3048
}
3149

3250
pub(crate) fn add(
3351
&mut self,
3452
command: &'static str,
3553
handler: impl Fn(Args) -> Result<()> + Send + Sync + 'static,
54+
) {
55+
self.add_protected(command, handler, |_| Ok(true));
56+
}
57+
58+
pub(crate) fn add_protected(
59+
&mut self,
60+
command: &'static str,
61+
handler: impl Fn(Args) -> Result<()> + Send + Sync + 'static,
62+
guard: GuardFn,
3663
) {
3764
info!("Adding command {}", &command);
3865
let mut state = 0;
@@ -89,7 +116,10 @@ impl Commands {
89116
}
90117
});
91118

92-
let handler = Arc::new(handler);
119+
let handler = Arc::new(Command {
120+
guard,
121+
ptr: Box::new(handler),
122+
});
93123

94124
if opt_lambda_state.is_some() {
95125
opt_final_states.iter().for_each(|state| {
@@ -100,31 +130,76 @@ impl Commands {
100130
self.state_machine.set_final_state(state);
101131
self.state_machine.set_handler(state, handler.clone());
102132
}
133+
}
134+
135+
pub(crate) fn help(
136+
&mut self,
137+
cmd: &'static str,
138+
desc: &'static str,
139+
handler: impl Fn(Args) -> Result<()> + Send + Sync + 'static,
140+
) {
141+
self.help_protected(cmd, desc, handler, |_| Ok(true));
142+
}
143+
144+
pub(crate) fn help_protected(
145+
&mut self,
146+
cmd: &'static str,
147+
desc: &'static str,
148+
handler: impl Fn(Args) -> Result<()> + Send + Sync + 'static,
149+
guard: GuardFn,
150+
) {
151+
let base_cmd = &cmd[1..];
152+
info!("Adding command ?help {}", &base_cmd);
153+
let mut state = 0;
103154

104155
self.menu.as_mut().map(|menu| {
105-
*menu += command;
106-
*menu += "\n"
156+
menu.insert(cmd, (desc, guard));
157+
menu
107158
});
159+
160+
state = add_help_menu(&mut self.state_machine, base_cmd, state);
161+
self.state_machine.set_final_state(state);
162+
self.state_machine.set_handler(
163+
state,
164+
Arc::new(Command {
165+
guard,
166+
ptr: Box::new(handler),
167+
}),
168+
);
108169
}
109170

110-
pub(crate) fn menu(&mut self) -> Option<String> {
111-
self.menu.as_mut().map(|menu| *menu += "?help\n");
171+
pub(crate) fn menu(&mut self) -> Option<HashMap<&'static str, (&'static str, GuardFn)>> {
112172
self.menu.take()
113173
}
114174

115175
pub(crate) fn execute<'m>(&'m self, cx: Context, msg: Message) {
116176
let message = &msg.content;
117177
if !msg.is_own(&cx) && message.starts_with(PREFIX) {
118178
self.state_machine.process(message).map(|matched| {
119-
info!("Executing command {}", message);
179+
info!("Processing command: {}", message);
120180
let args = Args {
121181
http: &self.client,
122182
cx: &cx,
123183
msg: &msg,
124184
params: matched.params,
125185
};
126-
if let Err(e) = (matched.handler)(args) {
127-
println!("{}", e);
186+
info!("Checking permissions");
187+
match matched.handler.authorize(&args) {
188+
Ok(true) => {
189+
info!("Executing command");
190+
if let Err(e) = matched.handler.call(args) {
191+
error!("{}", e);
192+
}
193+
}
194+
Ok(false) => {
195+
info!("Not executing command, unauthorized");
196+
if let Err(e) =
197+
api::send_reply(&args, "You do not have permission to run this command")
198+
{
199+
error!("{}", e);
200+
}
201+
}
202+
Err(e) => error!("{}", e),
128203
}
129204
});
130205
}
@@ -145,7 +220,7 @@ fn key_value_pair(s: &'static str) -> Option<&'static str> {
145220
.flatten()
146221
}
147222

148-
fn add_space(state_machine: &mut StateMachine, mut state: usize, i: usize) -> usize {
223+
fn add_space<T>(state_machine: &mut StateMachine<T>, mut state: usize, i: usize) -> usize {
149224
if i > 0 {
150225
let mut char_set = CharacterSet::from_char(' ');
151226
char_set.insert('\n');
@@ -156,8 +231,24 @@ fn add_space(state_machine: &mut StateMachine, mut state: usize, i: usize) -> us
156231
state
157232
}
158233

159-
fn add_dynamic_segment(
160-
state_machine: &mut StateMachine,
234+
fn add_help_menu<T>(
235+
mut state_machine: &mut StateMachine<T>,
236+
cmd: &'static str,
237+
mut state: usize,
238+
) -> usize {
239+
"?help".chars().for_each(|ch| {
240+
state = state_machine.add(state, CharacterSet::from_char(ch));
241+
});
242+
state = add_space(&mut state_machine, state, 1);
243+
cmd.chars().for_each(|ch| {
244+
state = state_machine.add(state, CharacterSet::from_char(ch));
245+
});
246+
247+
state
248+
}
249+
250+
fn add_dynamic_segment<T>(
251+
state_machine: &mut StateMachine<T>,
161252
name: &'static str,
162253
mut state: usize,
163254
) -> usize {
@@ -171,8 +262,8 @@ fn add_dynamic_segment(
171262
state
172263
}
173264

174-
fn add_remaining_segment(
175-
state_machine: &mut StateMachine,
265+
fn add_remaining_segment<T>(
266+
state_machine: &mut StateMachine<T>,
176267
name: &'static str,
177268
mut state: usize,
178269
) -> usize {
@@ -185,8 +276,8 @@ fn add_remaining_segment(
185276
state
186277
}
187278

188-
fn add_code_segment_multi_line(
189-
state_machine: &mut StateMachine,
279+
fn add_code_segment_multi_line<T>(
280+
state_machine: &mut StateMachine<T>,
190281
name: &'static str,
191282
mut state: usize,
192283
) -> usize {
@@ -219,8 +310,8 @@ fn add_code_segment_multi_line(
219310
state
220311
}
221312

222-
fn add_code_segment_single_line(
223-
state_machine: &mut StateMachine,
313+
fn add_code_segment_single_line<T>(
314+
state_machine: &mut StateMachine<T>,
224315
name: &'static str,
225316
mut state: usize,
226317
n_backticks: usize,
@@ -239,7 +330,11 @@ fn add_code_segment_single_line(
239330
state
240331
}
241332

242-
fn add_key_value(state_machine: &mut StateMachine, name: &'static str, mut state: usize) -> usize {
333+
fn add_key_value<T>(
334+
state_machine: &mut StateMachine<T>,
335+
name: &'static str,
336+
mut state: usize,
337+
) -> usize {
243338
name.chars().for_each(|c| {
244339
state = state_machine.add(state, CharacterSet::from_char(c));
245340
});

0 commit comments

Comments
 (0)