@@ -2,9 +2,7 @@ use std::time::Duration;
22
33use crate :: prelude:: * ;
44
5- pub struct RemindPlugin {
6- db_pool : sqlx:: SqlitePool ,
7- }
5+ pub struct RemindPlugin { }
86
97#[ derive( sqlx:: FromRow ) ]
108struct Reminder {
@@ -34,7 +32,12 @@ fn parse_duration(s: &str) -> Result<Duration> {
3432 "h" => num * 60 * 60 ,
3533 "d" => num * 60 * 60 * 24 ,
3634 "w" => num * 60 * 60 * 24 * 7 ,
37- _ => return Err ( format_err ! ( "Unknown duration unit '{}'. Use s/m/h/d/w" , unit) ) ,
35+ _ => {
36+ return Err ( format_err ! (
37+ "Unknown duration unit '{}'. Use s/m/h/d/w" ,
38+ unit
39+ ) )
40+ }
3841 } ;
3942
4043 Ok ( Duration :: from_secs ( seconds) )
@@ -57,11 +60,17 @@ fn format_duration(secs: i64) -> String {
5760}
5861
5962impl RemindPlugin {
63+ fn new ( ) -> Self {
64+ RemindPlugin { }
65+ }
66+
6067 async fn handle_list ( & self , ctx : & Arc < Context > ) -> Result < ( ) > {
6168 let sender = ctx
6269 . sender ( )
6370 . ok_or_else ( || format_err ! ( "Could not determine sender" ) ) ?;
6471
72+ let db = ctx. get_db ( ) ;
73+
6574 let reminders: Vec < Reminder > = sqlx:: query_as!(
6675 Reminder ,
6776 r#"SELECT id as "id!", channel_id, target_user, message, remind_at, created_at, created_by
@@ -71,7 +80,7 @@ impl RemindPlugin {
7180 LIMIT 10"# ,
7281 sender
7382 )
74- . fetch_all ( & self . db_pool )
83+ . fetch_all ( & db )
7584 . await ?;
7685
7786 if reminders. is_empty ( ) {
@@ -92,11 +101,15 @@ impl RemindPlugin {
92101 } else {
93102 r. target_user . clone ( )
94103 } ;
95- format ! ( "[{}] in {} for {}: \" {}\" " , r. id, time_left, target, r. message)
104+ format ! (
105+ "[{}] in {} for {}: \" {}\" " ,
106+ r. id, time_left, target, r. message
107+ )
96108 } )
97109 . collect ( ) ;
98110
99111 ctx. mention_reply ( & lines. join ( " | " ) ) . await ?;
112+
100113 Ok ( ( ) )
101114 }
102115
@@ -108,74 +121,40 @@ impl RemindPlugin {
108121 let id: i64 = match id_str. trim ( ) . parse ( ) {
109122 Ok ( id) => id,
110123 Err ( _) => {
111- ctx. mention_reply ( "Invalid reminder ID. Use 'remind list' to see your reminders." ) . await ?;
124+ ctx. mention_reply ( "Invalid reminder ID. Use 'remind list' to see your reminders." )
125+ . await ?;
112126 return Ok ( ( ) ) ;
113127 }
114128 } ;
115129
130+ let db = ctx. get_db ( ) ;
131+
116132 let result = sqlx:: query!(
117133 "DELETE FROM reminders WHERE id = $1 AND (created_by = $2 OR target_user = $2)" ,
118134 id,
119135 sender
120136 )
121- . execute ( & self . db_pool )
137+ . execute ( & db )
122138 . await ?;
123139
124140 if result. rows_affected ( ) == 0 {
125- ctx. mention_reply ( "Reminder not found or you don't have permission to cancel it." ) . await ?;
141+ ctx. mention_reply ( "Reminder not found or you don't have permission to cancel it." )
142+ . await ?;
126143 } else {
127144 ctx. mention_reply ( "Reminder cancelled." ) . await ?;
128145 }
129146
130147 Ok ( ( ) )
131148 }
132149
133- async fn handle_remind ( & self , ctx : & Arc < Context > , arg : Option < & str > ) -> Result < ( ) > {
134- let arg = match arg {
135- Some ( a) => a,
136- None => {
137- ctx. mention_reply ( "Usage: remind <user|me> <time> <message> | remind list | remind cancel <id>" ) . await ?;
138- return Ok ( ( ) ) ;
139- }
140- } ;
141-
142- let parts: Vec < & str > = arg. splitn ( 3 , ' ' ) . collect ( ) ;
143-
144- // Handle subcommands
145- if parts[ 0 ] . eq_ignore_ascii_case ( "list" ) {
146- return self . handle_list ( ctx) . await ;
147- }
148- if parts[ 0 ] . eq_ignore_ascii_case ( "cancel" ) {
149- if parts. len ( ) < 2 {
150- ctx. mention_reply ( "Usage: remind cancel <id>" ) . await ?;
151- return Ok ( ( ) ) ;
152- }
153- return self . handle_cancel ( ctx, parts[ 1 ] ) . await ;
154- }
155- if parts. len ( ) < 3 {
156- ctx. mention_reply ( "Usage: remind <user|me> <time> <message>" ) . await ?;
157- return Ok ( ( ) ) ;
158- }
159-
160- let sender = ctx
161- . sender ( )
162- . ok_or_else ( || format_err ! ( "Could not determine sender" ) ) ?;
163-
164- let target_user = if parts[ 0 ] . eq_ignore_ascii_case ( "me" ) {
165- sender. to_string ( )
166- } else {
167- parts[ 0 ] . to_string ( )
168- } ;
169- let duration_str = parts[ 1 ] ;
170- let message = parts[ 2 ] ;
171-
172- let duration = match parse_duration ( duration_str) {
173- Ok ( d) => d,
174- Err ( e) => {
175- ctx. mention_reply ( & format ! ( "{}" , e) ) . await ?;
176- return Ok ( ( ) ) ;
177- }
178- } ;
150+ async fn handle_add (
151+ & self ,
152+ ctx : & Arc < Context > ,
153+ target : & str ,
154+ duration : std:: time:: Duration ,
155+ message : & str ,
156+ ) -> Result < ( ) > {
157+ let sender = ctx. sender ( ) . unwrap_or ( "unknown sender" ) ;
179158
180159 let channel_id = ctx
181160 . target_channel_id ( )
@@ -186,22 +165,24 @@ impl RemindPlugin {
186165 . as_secs ( ) as i64 ;
187166 let remind_at = now + duration. as_secs ( ) as i64 ;
188167
168+ let db = ctx. get_db ( ) ;
169+
189170 sqlx:: query!(
190171 "INSERT INTO reminders (channel_id, target_user, message, remind_at, created_at, created_by) VALUES ($1, $2, $3, $4, $5, $6)" ,
191172 channel_id,
192- target_user ,
173+ target ,
193174 message,
194175 remind_at,
195176 now,
196177 sender
197178 )
198- . execute ( & self . db_pool )
179+ . execute ( & db )
199180 . await ?;
200181
201182 let duration_text = format_duration ( duration. as_secs ( ) as i64 ) ;
202183 ctx. mention_reply ( & format ! (
203184 "I'll remind {} in {}: \" {}\" " ,
204- if target_user == sender { "you" . to_string ( ) } else { target_user } ,
185+ if target == sender { "you" } else { target } ,
205186 duration_text,
206187 message
207188 ) )
@@ -210,23 +191,92 @@ impl RemindPlugin {
210191 Ok ( ( ) )
211192 }
212193
213- async fn check_due_reminders ( & self , bot : & Arc < Client > ) -> Result < ( ) > {
194+ async fn handle_remind ( & self , ctx : & Arc < Context > , arg : Option < & str > ) -> Result < ( ) > {
195+ let arg = match arg {
196+ Some ( a) => a,
197+ None => {
198+ ctx. mention_reply (
199+ "Usage: remind <user|me> <time> <message> | remind list | remind cancel <id>" ,
200+ )
201+ . await ?;
202+ return Ok ( ( ) ) ;
203+ }
204+ } ;
205+
206+ let mut parts = arg. splitn ( 2 , ' ' ) ;
207+ let first_arg = parts. next ( ) . unwrap_or ( "" ) ;
208+ let rest = parts. next ( ) ;
209+
210+ match first_arg {
211+ "list" => {
212+ self . handle_list ( ctx) . await ?;
213+ return Ok ( ( ) ) ;
214+ }
215+ "cancel" => {
216+ let id_str = match rest {
217+ None => {
218+ ctx. mention_reply ( "Usage: remind cancel <id>" ) . await ?;
219+ return Ok ( ( ) ) ;
220+ }
221+ Some ( id_str) => id_str,
222+ } ;
223+
224+ self . handle_cancel ( ctx, id_str) . await ?;
225+ return Ok ( ( ) ) ;
226+ }
227+ arg => {
228+ let ( duration, message) = match rest. unwrap_or ( "" ) . split_once ( ' ' ) {
229+ None => {
230+ ctx. mention_reply ( "Usage: remind <user|me> <time> <message>" )
231+ . await ?;
232+ return Ok ( ( ) ) ;
233+ }
234+ Some ( ( time, message) ) => ( time, message) ,
235+ } ;
236+
237+ let target = if arg. eq_ignore_ascii_case ( "me" ) {
238+ ctx. sender ( ) . unwrap_or ( "unknown sender" )
239+ } else {
240+ arg
241+ } ;
242+
243+ let duration = match parse_duration ( duration) {
244+ Err ( e) => {
245+ ctx. mention_reply ( & format ! ( "{}" , e) ) . await ?;
246+ return Ok ( ( ) ) ;
247+ }
248+ Ok ( d) => d,
249+ } ;
250+
251+ self . handle_add ( ctx, target, duration, message) . await ?;
252+
253+ return Ok ( ( ) ) ;
254+ }
255+ }
256+ }
257+
258+ async fn check_due_reminders ( & self , bot : & Client ) -> Result < ( ) > {
214259 let now = std:: time:: SystemTime :: now ( )
215260 . duration_since ( std:: time:: UNIX_EPOCH ) ?
216261 . as_secs ( ) as i64 ;
217262
263+ let db = bot. get_db ( ) ;
264+
218265 let reminders: Vec < Reminder > = sqlx:: query_as!(
219266 Reminder ,
220267 r#"SELECT id as "id!", channel_id, target_user, message, remind_at, created_at, created_by FROM reminders WHERE remind_at <= $1"# ,
221268 now
222269 )
223- . fetch_all ( & self . db_pool )
270+ . fetch_all ( & db )
224271 . await ?;
225272
226273 for reminder in reminders {
227274 let age = format_duration ( now - reminder. created_at ) ;
228275 let msg = if reminder. created_by == reminder. target_user {
229- format ! ( "{}: Reminder ({} ago): {}" , reminder. target_user, age, reminder. message)
276+ format ! (
277+ "{}: Reminder ({} ago): {}" ,
278+ reminder. target_user, age, reminder. message
279+ )
230280 } else {
231281 format ! (
232282 "{}: Reminder from {} ({} ago): {}" ,
@@ -240,7 +290,7 @@ impl RemindPlugin {
240290 }
241291
242292 sqlx:: query!( "DELETE FROM reminders WHERE id = $1" , reminder. id)
243- . execute ( & self . db_pool )
293+ . execute ( & db )
244294 . await ?;
245295 }
246296
@@ -251,12 +301,7 @@ impl RemindPlugin {
251301#[ async_trait]
252302impl Plugin for RemindPlugin {
253303 fn new_from_env ( ) -> Result < Self > {
254- let db_url = dotenvy:: var ( "DATABASE_URL" )
255- . map_err ( |_| format_err ! ( "Missing $DATABASE_URL" ) ) ?;
256- let db_pool = sqlx:: sqlite:: SqlitePoolOptions :: new ( )
257- . max_connections ( 2 )
258- . connect_lazy ( & db_url) ?;
259- Ok ( RemindPlugin { db_pool } )
304+ Ok ( RemindPlugin :: new ( ) )
260305 }
261306
262307 fn command_metadata ( & self ) -> Vec < CommandMetadata > {
0 commit comments