Skip to content

Commit 396305f

Browse files
committed
feat(commands): add command to subcribe or unsubcribe to role's ping from reminder command
1 parent 22ca44c commit 396305f

5 files changed

Lines changed: 258 additions & 0 deletions

File tree

crates/rustmail/src/bot.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub async fn run_bot(
140140
registry.register_command(ReplyCommand);
141141
registry.register_command(AddReminderCommand);
142142
registry.register_command(RemoveReminderCommand);
143+
registry.register_command(ReminderSubscriptionCommand);
143144
registry.register_command(LogsCommand);
144145
registry.register_command(TakeCommand);
145146
registry.register_command(ReleaseCommand);

crates/rustmail/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub mod new_thread;
2525
pub mod ping;
2626
pub mod recover;
2727
pub mod release;
28+
pub mod reminder_subscription;
2829
pub mod remove_reminder;
2930
pub mod remove_staff;
3031
pub mod reply;
@@ -48,6 +49,7 @@ pub use new_thread::*;
4849
pub use ping::*;
4950
pub use recover::*;
5051
pub use release::*;
52+
pub use reminder_subscription::*;
5153
pub use remove_reminder::*;
5254
pub use remove_staff::*;
5355
pub use reply::*;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod slash_command;
2+
3+
pub use slash_command::*;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod reminder_subscription;
2+
3+
pub use reminder_subscription::*;
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
use crate::prelude::commands::*;
2+
use crate::prelude::config::*;
3+
use crate::prelude::db::*;
4+
use crate::prelude::errors::*;
5+
use crate::prelude::handlers::*;
6+
use crate::prelude::i18n::*;
7+
use crate::prelude::utils::*;
8+
use serenity::FutureExt;
9+
use serenity::all::{
10+
CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand,
11+
CreateCommandOption, GuildId, ResolvedOption, RoleId,
12+
};
13+
use std::collections::HashMap;
14+
use std::sync::Arc;
15+
16+
pub struct ReminderSubscriptionCommand;
17+
18+
#[async_trait::async_trait]
19+
impl RegistrableCommand for ReminderSubscriptionCommand {
20+
fn name(&self) -> &'static str {
21+
"reminder_subscription"
22+
}
23+
24+
fn doc<'a>(&self, config: &'a Config) -> BoxFuture<'a, String> {
25+
async move {
26+
get_translated_message(config, "help.reminder_subscription", None, None, None, None)
27+
.await
28+
}
29+
.boxed()
30+
}
31+
32+
fn register(&self, config: &Config) -> BoxFuture<'_, Vec<CreateCommand>> {
33+
let config = config.clone();
34+
let name = self.name();
35+
36+
Box::pin(async move {
37+
let cmd_desc = get_translated_message(
38+
&config,
39+
"slash_command.reminder_subscribe_description",
40+
None,
41+
None,
42+
None,
43+
None,
44+
)
45+
.await;
46+
let action_desc = get_translated_message(
47+
&config,
48+
"slash_command.reminder_action_argument",
49+
None,
50+
None,
51+
None,
52+
None,
53+
)
54+
.await;
55+
let role_desc = get_translated_message(
56+
&config,
57+
"slash_command.reminder_role_argument",
58+
None,
59+
None,
60+
None,
61+
None,
62+
)
63+
.await;
64+
65+
vec![
66+
CreateCommand::new(name)
67+
.description(cmd_desc)
68+
.add_option(
69+
CreateCommandOption::new(CommandOptionType::String, "action", action_desc)
70+
.required(true)
71+
.add_string_choice("subscribe", "subscribe")
72+
.add_string_choice("unsubscribe", "unsubscribe"),
73+
)
74+
.add_option(
75+
CreateCommandOption::new(CommandOptionType::Role, "role", role_desc)
76+
.required(true),
77+
),
78+
]
79+
})
80+
}
81+
82+
fn run(
83+
&self,
84+
ctx: &Context,
85+
command: &CommandInteraction,
86+
_options: &[ResolvedOption<'_>],
87+
config: &Config,
88+
_handler: Arc<InteractionHandler>,
89+
) -> BoxFuture<'_, ModmailResult<()>> {
90+
let ctx = ctx.clone();
91+
let command = command.clone();
92+
let config = config.clone();
93+
94+
Box::pin(async move {
95+
let pool = config
96+
.db_pool
97+
.as_ref()
98+
.ok_or_else(database_connection_failed)?;
99+
100+
let _ = defer_response(&ctx, &command).await;
101+
102+
let mut action: Option<String> = None;
103+
let mut role_id: Option<RoleId> = None;
104+
105+
for option in &command.data.options {
106+
match option.name.as_str() {
107+
"action" => {
108+
if let CommandDataOptionValue::String(val) = &option.value {
109+
action.replace(val.clone());
110+
}
111+
}
112+
"role" => {
113+
if let CommandDataOptionValue::Role(val) = &option.value {
114+
role_id.replace(*val);
115+
}
116+
}
117+
_ => {}
118+
}
119+
}
120+
121+
let action =
122+
action.ok_or_else(|| ModmailError::Command(CommandError::MissingArguments))?;
123+
124+
let role_id =
125+
role_id.ok_or_else(|| ModmailError::Command(CommandError::MissingArguments))?;
126+
127+
let is_subscribe = action == "subscribe";
128+
129+
// Get the guild
130+
let guild_id = config.bot.get_staff_guild_id();
131+
let guild_id_obj = GuildId::new(guild_id);
132+
let guild = guild_id_obj
133+
.to_partial_guild(&ctx.http)
134+
.await
135+
.map_err(|_| {
136+
ModmailError::Discord(DiscordError::ApiError("Guild not found".to_string()))
137+
})?;
138+
139+
// Get the role name
140+
let role = guild.roles.get(&role_id).ok_or_else(|| {
141+
ModmailError::Discord(DiscordError::ApiError("Role not found".to_string()))
142+
})?;
143+
144+
let role_name = role.name.clone();
145+
146+
// Check if the user has the role
147+
let member = guild_id_obj
148+
.member(&ctx.http, command.user.id)
149+
.await
150+
.map_err(|_| ModmailError::Discord(DiscordError::UserNotFound))?;
151+
152+
if !member.roles.contains(&role_id) {
153+
let mut params = HashMap::new();
154+
params.insert("role".to_string(), role_name.clone());
155+
156+
let _ = MessageBuilder::system_message(&ctx, &config)
157+
.translated_content(
158+
"reminder_subscription.role_required",
159+
Some(&params),
160+
Some(command.user.id),
161+
command.guild_id.map(|g| g.get()),
162+
)
163+
.await
164+
.to_channel(command.channel_id)
165+
.send_interaction_followup(&command, true)
166+
.await;
167+
return Ok(());
168+
}
169+
170+
// Perform the subscription/unsubscription
171+
let mut params = HashMap::new();
172+
params.insert("role".to_string(), role_name.clone());
173+
174+
if is_subscribe {
175+
// Re-subscribe: delete the opt-out record
176+
let was_opted_out = delete_reminder_optout(
177+
guild_id as i64,
178+
command.user.id.get() as i64,
179+
role_id.get() as i64,
180+
pool,
181+
)
182+
.await?;
183+
184+
let message_key = if was_opted_out {
185+
"reminder_subscription.subscribed"
186+
} else {
187+
"reminder_subscription.already_subscribed"
188+
};
189+
190+
let _ = MessageBuilder::system_message(&ctx, &config)
191+
.translated_content(
192+
message_key,
193+
Some(&params),
194+
Some(command.user.id),
195+
command.guild_id.map(|g| g.get()),
196+
)
197+
.await
198+
.to_channel(command.channel_id)
199+
.send_interaction_followup(&command, true)
200+
.await;
201+
} else {
202+
// Unsubscribe: insert an opt-out record
203+
let is_already_opted_out = is_user_opted_out(
204+
guild_id as i64,
205+
command.user.id.get() as i64,
206+
role_id.get() as i64,
207+
pool,
208+
)
209+
.await?;
210+
211+
if is_already_opted_out {
212+
let _ = MessageBuilder::system_message(&ctx, &config)
213+
.translated_content(
214+
"reminder_subscription.already_unsubscribed",
215+
Some(&params),
216+
Some(command.user.id),
217+
command.guild_id.map(|g| g.get()),
218+
)
219+
.await
220+
.to_channel(command.channel_id)
221+
.send_interaction_followup(&command, true)
222+
.await;
223+
} else {
224+
insert_reminder_optout(
225+
guild_id as i64,
226+
command.user.id.get() as i64,
227+
role_id.get() as i64,
228+
pool,
229+
)
230+
.await?;
231+
232+
let _ = MessageBuilder::system_message(&ctx, &config)
233+
.translated_content(
234+
"reminder_subscription.unsubscribed",
235+
Some(&params),
236+
Some(command.user.id),
237+
command.guild_id.map(|g| g.get()),
238+
)
239+
.await
240+
.to_channel(command.channel_id)
241+
.send_interaction_followup(&command, true)
242+
.await;
243+
}
244+
}
245+
246+
Ok(())
247+
})
248+
}
249+
}

0 commit comments

Comments
 (0)