Skip to content

Commit 045a62a

Browse files
committed
feat(commands): add remove_reminder command
1 parent 156330d commit 045a62a

15 files changed

Lines changed: 304 additions & 5 deletions

File tree

src/commands/add_reminder/common.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::config::Config;
2-
use crate::db::reminders::{Reminder, update_reminder_status};
2+
use crate::db::reminders::{Reminder, is_reminder_active, update_reminder_status};
33
use crate::utils::conversion::hex_string_to_int::hex_string_to_int;
44
use crate::utils::message::message_builder::MessageBuilder;
55
use chrono::Local;
@@ -109,7 +109,13 @@ pub async fn send_register_confirmation_from_command(
109109
}
110110
}
111111

112-
pub fn spawn_reminder(reminder: &Reminder, ctx: &Context, config: &Config, pool: &SqlitePool) {
112+
pub fn spawn_reminder(
113+
reminder: &Reminder,
114+
reminder_id: Option<i64>,
115+
ctx: &Context,
116+
config: &Config,
117+
pool: &SqlitePool,
118+
) {
113119
let pool = pool.clone();
114120
let config = config.clone();
115121
let ctx = ctx.clone();
@@ -124,6 +130,20 @@ pub fn spawn_reminder(reminder: &Reminder, ctx: &Context, config: &Config, pool:
124130
};
125131
sleep(Duration::from_secs(delay_duration as u64)).await;
126132

133+
if let Some(reminder_id) = reminder_id {
134+
match is_reminder_active(reminder_id, &pool).await {
135+
Ok(active) => {
136+
if !active {
137+
return;
138+
}
139+
}
140+
Err(e) => {
141+
eprintln!("Failed to check reminder status: {}", e);
142+
return;
143+
}
144+
}
145+
}
146+
127147
let mut params = HashMap::new();
128148
params.insert(
129149
"time".to_string(),

src/commands/add_reminder/slash_command/add_reminder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl RegistrableCommand for AddReminderCommand {
199199
)
200200
.await;
201201

202-
spawn_reminder(&reminder, &ctx, &config, &pool);
202+
spawn_reminder(&reminder, Some(reminder_id), &ctx, &config, &pool);
203203

204204
Ok(())
205205
})

src/commands/add_reminder/text_command/add_reminder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub async fn add_reminder(ctx: &Context, msg: &Message, config: &Config) -> Modm
100100

101101
let _ = msg.delete(&ctx.http).await;
102102

103-
spawn_reminder(&reminder, &ctx, &config, &pool);
103+
spawn_reminder(&reminder, Some(reminder_id), &ctx, &config, &pool);
104104

105105
Ok(())
106106
}

src/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod id;
1818
pub mod move_thread;
1919
pub mod new_thread;
2020
pub mod recover;
21+
pub mod remove_reminder;
2122
pub mod remove_staff;
2223
pub mod reply;
2324

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod slash_command;
2+
pub mod text_command;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod remove_reminder;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use crate::commands::{BoxFuture, RegistrableCommand};
2+
use crate::config::Config;
3+
use crate::db::reminders::{get_reminder_by_id, update_reminder_status};
4+
use crate::errors::{CommandError, DatabaseError, ModmailError, ModmailResult, common};
5+
use crate::i18n::get_translated_message;
6+
use crate::utils::command::defer_response::defer_response;
7+
use crate::utils::message::message_builder::MessageBuilder;
8+
use serenity::all::{
9+
CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand,
10+
CreateCommandOption, ResolvedOption,
11+
};
12+
use std::collections::HashMap;
13+
14+
pub struct RemoveReminderCommand;
15+
16+
impl RegistrableCommand for RemoveReminderCommand {
17+
fn name(&self) -> &'static str {
18+
"remove_reminder"
19+
}
20+
21+
fn register(&self, config: &Config) -> BoxFuture<Vec<CreateCommand>> {
22+
let config = config.clone();
23+
let name = self.name();
24+
25+
Box::pin(async move {
26+
let cmd_desc = get_translated_message(
27+
&config,
28+
"slash_command.remove_reminder_command_description",
29+
None,
30+
None,
31+
None,
32+
None,
33+
)
34+
.await;
35+
let id_desc = get_translated_message(
36+
&config,
37+
"slash_command.remove_reminder_id_argument",
38+
None,
39+
None,
40+
None,
41+
None,
42+
)
43+
.await;
44+
45+
vec![CreateCommand::new(name).description(cmd_desc).add_option(
46+
CreateCommandOption::new(CommandOptionType::Number, "id", id_desc).required(true),
47+
)]
48+
})
49+
}
50+
51+
fn run(
52+
&self,
53+
ctx: &Context,
54+
command: &CommandInteraction,
55+
options: &[ResolvedOption<'_>],
56+
config: &Config,
57+
) -> BoxFuture<ModmailResult<()>> {
58+
let ctx = ctx.clone();
59+
let command = command.clone();
60+
let config = config.clone();
61+
62+
Box::pin(async move {
63+
let pool = config
64+
.db_pool
65+
.as_ref()
66+
.ok_or_else(common::database_connection_failed)?;
67+
68+
let _ = defer_response(&ctx, &command).await;
69+
70+
let mut reminder_id: Option<i64> = None;
71+
72+
for option in &command.data.options {
73+
match option.name.as_str() {
74+
"id" => {
75+
if let CommandDataOptionValue::Number(val) = &option.value {
76+
reminder_id.replace(*val as i64);
77+
}
78+
}
79+
_ => {}
80+
}
81+
}
82+
83+
let reminder_id = match reminder_id {
84+
Some(id) => id,
85+
None => {
86+
return Err(ModmailError::Command(CommandError::InvalidArguments(
87+
"reminder ID".to_string(),
88+
)));
89+
}
90+
};
91+
92+
let reminder = match get_reminder_by_id(reminder_id, pool).await {
93+
Ok(Some(r)) => r,
94+
Ok(None) => {
95+
return Err(ModmailError::Database(DatabaseError::NotFound(
96+
"".to_string(),
97+
)));
98+
}
99+
Err(e) => {
100+
return Err(ModmailError::Database(DatabaseError::QueryFailed(
101+
e.to_string(),
102+
)));
103+
}
104+
};
105+
106+
match update_reminder_status(&reminder, true, pool).await {
107+
Ok(_) => {
108+
let mut params = HashMap::new();
109+
params.insert("id".to_string(), reminder_id.to_string());
110+
111+
let response = MessageBuilder::system_message(&ctx, &config)
112+
.translated_content(
113+
"remove_reminder.confirmation",
114+
Some(&params),
115+
None,
116+
None,
117+
)
118+
.await
119+
.to_channel(command.channel_id)
120+
.build_interaction_message_followup()
121+
.await;
122+
123+
let _ = command.create_followup(&ctx.http, response).await;
124+
}
125+
Err(e) => {
126+
return Err(ModmailError::Database(DatabaseError::QueryFailed(
127+
e.to_string(),
128+
)));
129+
}
130+
}
131+
132+
Ok(())
133+
})
134+
}
135+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod remove_reminder;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::config::Config;
2+
use crate::db::reminders::{get_reminder_by_id, update_reminder_status};
3+
use crate::errors::{CommandError, DatabaseError, ModmailError, ModmailResult, common};
4+
use crate::utils::command::extract_reply_content::extract_reply_content;
5+
use crate::utils::message::message_builder::MessageBuilder;
6+
use serenity::all::{Context, Message};
7+
use std::collections::HashMap;
8+
9+
pub async fn remove_reminder(ctx: &Context, msg: &Message, config: &Config) -> ModmailResult<()> {
10+
let pool = config
11+
.db_pool
12+
.as_ref()
13+
.ok_or_else(common::database_connection_failed)?;
14+
15+
let content = match extract_reply_content(
16+
&msg.content,
17+
&config.command.prefix,
18+
&["remove_reminder", "rr"],
19+
) {
20+
Some(c) => c,
21+
None => {
22+
return Err(ModmailError::Command(CommandError::InvalidArguments(
23+
"".to_string(),
24+
)));
25+
}
26+
};
27+
28+
let reminder_id = match content.parse::<u64>() {
29+
Ok(id) => id,
30+
Err(_) => {
31+
return Err(ModmailError::Command(CommandError::InvalidArguments(
32+
"Reminder ID".to_string(),
33+
)));
34+
}
35+
};
36+
37+
let reminder = match get_reminder_by_id(reminder_id as i64, pool).await {
38+
Ok(Some(r)) => r,
39+
Ok(None) => {
40+
return Err(ModmailError::Database(DatabaseError::NotFound(
41+
"".to_string(),
42+
)));
43+
}
44+
Err(e) => {
45+
return Err(ModmailError::Database(DatabaseError::QueryFailed(
46+
e.to_string(),
47+
)));
48+
}
49+
};
50+
51+
match update_reminder_status(&reminder, true, pool).await {
52+
Ok(_) => {
53+
let mut params = HashMap::new();
54+
params.insert("id".to_string(), reminder_id.to_string());
55+
56+
let _ = MessageBuilder::system_message(&ctx, &config)
57+
.translated_content("remove_reminder.confirmation", Some(&params), None, None)
58+
.await
59+
.to_channel(msg.channel_id)
60+
.send()
61+
.await;
62+
}
63+
Err(e) => {
64+
return Err(ModmailError::Database(DatabaseError::QueryFailed(
65+
e.to_string(),
66+
)));
67+
}
68+
}
69+
70+
Ok(())
71+
}

src/db/operations/reminders.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,43 @@ pub async fn get_all_pending_reminders(
7474
.await?;
7575
Ok(rows)
7676
}
77+
78+
pub async fn get_reminder_by_id(
79+
reminder_id: i64,
80+
pool: &sqlx::SqlitePool,
81+
) -> Result<Option<Reminder>, sqlx::Error> {
82+
let row = sqlx::query_as!(
83+
Reminder,
84+
r#"
85+
SELECT thread_id, user_id, channel_id, guild_id, reminder_content, trigger_time, created_at, completed
86+
FROM reminders
87+
WHERE id = ?
88+
"#,
89+
reminder_id
90+
)
91+
.fetch_optional(pool)
92+
.await?;
93+
Ok(row)
94+
}
95+
96+
pub async fn is_reminder_active(
97+
reminder_id: i64,
98+
pool: &sqlx::SqlitePool,
99+
) -> Result<bool, sqlx::Error> {
100+
let row = sqlx::query!(
101+
r#"
102+
SELECT completed
103+
FROM reminders
104+
WHERE id = ?
105+
"#,
106+
reminder_id
107+
)
108+
.fetch_optional(pool)
109+
.await?;
110+
111+
if let Some(record) = row {
112+
Ok(!record.completed)
113+
} else {
114+
Ok(false)
115+
}
116+
}

0 commit comments

Comments
 (0)