@@ -2,9 +2,9 @@ use crate::prelude::config::*;
22use crate :: prelude:: db:: * ;
33use crate :: prelude:: utils:: * ;
44use chrono:: Local ;
5- use serenity:: all:: { ChannelId , CommandInteraction , Context , Message , UserId } ;
5+ use serenity:: all:: { ChannelId , CommandInteraction , Context , GuildId , Message , RoleId , UserId } ;
66use sqlx:: SqlitePool ;
7- use std:: collections:: HashMap ;
7+ use std:: collections:: { HashMap , HashSet } ;
88use std:: sync:: Arc ;
99use std:: time:: Duration ;
1010use tokio:: select;
@@ -162,12 +162,46 @@ pub fn spawn_reminder(
162162 params. insert ( "user" . to_string ( ) , reminder. user_id . to_string ( ) ) ;
163163 params. insert ( "content" . to_string ( ) , reminder. reminder_content . to_string ( ) ) ;
164164
165- let mut mentions = Vec :: < UserId > :: new ( ) ;
166- mentions. push ( UserId :: new ( reminder. user_id as u64 ) ) ;
165+ let ( mentions, is_role_targeted) = if let Some ( ref target_roles_str) = reminder. target_roles
166+ {
167+ let role_mentions: String = target_roles_str
168+ . split ( ',' )
169+ . filter_map ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
170+ . map ( |id| format ! ( "<@&{}>" , id) )
171+ . collect :: < Vec < _ > > ( )
172+ . join ( ", " ) ;
173+ params. insert ( "roles" . to_string ( ) , role_mentions) ;
174+
175+ let members =
176+ get_targeted_mentions ( & ctx, & pool, reminder. guild_id as u64 , target_roles_str)
177+ . await ;
178+ ( members, true )
179+ } else {
180+ ( vec ! [ UserId :: new( reminder. user_id as u64 ) ] , false )
181+ } ;
182+
183+ if mentions. is_empty ( ) {
184+ if let Err ( e) = update_reminder_status ( & reminder, true , & pool) . await {
185+ eprintln ! ( "Failed to update reminder status: {}" , e) ;
186+ }
187+ return ;
188+ }
189+
190+ let ( key_with_content, key_without_content) = if is_role_targeted {
191+ (
192+ "reminder.show_with_content_roles" ,
193+ "reminder.show_without_content_roles" ,
194+ )
195+ } else {
196+ (
197+ "reminder.show_with_content" ,
198+ "reminder.show_without_content" ,
199+ )
200+ } ;
167201
168202 if !reminder. reminder_content . is_empty ( ) {
169203 let _ = MessageBuilder :: system_message ( & ctx, & config)
170- . translated_content ( "reminder.show_with_content" , Some ( & params) , None , None )
204+ . translated_content ( key_with_content , Some ( & params) , None , None )
171205 . await
172206 . to_channel ( ChannelId :: new ( reminder. channel_id as u64 ) )
173207 . color ( hex_string_to_int ( & config. reminders . embed_color ) as u32 )
@@ -176,7 +210,7 @@ pub fn spawn_reminder(
176210 . await ;
177211 } else {
178212 let _ = MessageBuilder :: system_message ( & ctx, & config)
179- . translated_content ( "reminder.show_without_content" , Some ( & params) , None , None )
213+ . translated_content ( key_without_content , Some ( & params) , None , None )
180214 . await
181215 . to_channel ( ChannelId :: new ( reminder. channel_id as u64 ) )
182216 . color ( hex_string_to_int ( & config. reminders . embed_color ) as u32 )
@@ -190,3 +224,60 @@ pub fn spawn_reminder(
190224 }
191225 } ) ;
192226}
227+
228+ async fn get_targeted_mentions (
229+ ctx : & Context ,
230+ pool : & SqlitePool ,
231+ guild_id : u64 ,
232+ target_roles_str : & str ,
233+ ) -> Vec < UserId > {
234+ let guild_id_obj = GuildId :: new ( guild_id) ;
235+
236+ let role_ids: Vec < u64 > = target_roles_str
237+ . split ( ',' )
238+ . filter_map ( |s| s. trim ( ) . parse :: < u64 > ( ) . ok ( ) )
239+ . collect ( ) ;
240+
241+ if role_ids. is_empty ( ) {
242+ return vec ! [ ] ;
243+ }
244+
245+ let members = match guild_id_obj. members ( & ctx. http , None , None ) . await {
246+ Ok ( m) => m,
247+ Err ( e) => {
248+ eprintln ! ( "Failed to fetch guild members: {}" , e) ;
249+ return vec ! [ ] ;
250+ }
251+ } ;
252+
253+ let mut user_ids_with_roles: HashSet < UserId > = HashSet :: new ( ) ;
254+
255+ for role_id in & role_ids {
256+ let role_id_obj = RoleId :: new ( * role_id) ;
257+ for member in & members {
258+ if member. roles . contains ( & role_id_obj) {
259+ user_ids_with_roles. insert ( member. user . id ) ;
260+ }
261+ }
262+ }
263+
264+ let mut opted_out_users: HashSet < u64 > = HashSet :: new ( ) ;
265+
266+ for role_id in & role_ids {
267+ match get_optouts_for_role ( guild_id as i64 , * role_id as i64 , pool) . await {
268+ Ok ( optouts) => {
269+ for user_id in optouts {
270+ opted_out_users. insert ( user_id as u64 ) ;
271+ }
272+ }
273+ Err ( e) => {
274+ eprintln ! ( "Failed to get optouts for role {}: {}" , role_id, e) ;
275+ }
276+ }
277+ }
278+
279+ user_ids_with_roles
280+ . into_iter ( )
281+ . filter ( |user_id| !opted_out_users. contains ( & user_id. get ( ) ) )
282+ . collect ( )
283+ }
0 commit comments