package events import ( "context" "fmt" "log" "strconv" "time" "velox-bot/internal/db/services" "github.com/bwmarrin/discordgo" ) // StartScheduleReminderLoop runs a background loop that periodically checks for // upcoming sessions and sends DM reminders to all participants. func StartScheduleReminderLoop(s *discordgo.Session, svc *services.Services) { if svc == nil || svc.Schedule == nil || s == nil { return } const ( interval = time.Minute // check every minute remindBefore = 10 * time.Minute // remind 10 minutes before initialDelay = 10 * time.Second ) go func() { // small delay so the bot can fully start time.Sleep(initialDelay) ticker := time.NewTicker(interval) defer ticker.Stop() for range ticker.C { now := time.Now().UTC() ctx := context.Background() // Find all sessions that start in the next `remindBefore` window and haven't been reminded. schedules, err := svc.Schedule.ListDueForReminder(ctx, now, remindBefore) if err != nil { log.Printf("schedule: reminder query failed: %v", err) continue } for _, sch := range schedules { if sch == nil { continue } // Build reminder text whenStr := sch.ScheduledAt.UTC().Format("2006-01-02 15:04") base := fmt.Sprintf("Reminder: you have a scheduled session at %s UTC with <@%d>.", whenStr, sch.InviteeID) desc := sch.Description if desc != "" { base += "\nTopic: " + desc } // Notify requester if err := dmUser(s, sch.RequesterID, base); err != nil { log.Printf("schedule: failed to DM requester reminder: %v", err) } // Notify invitee (mirror text with requester mention) baseInvitee := fmt.Sprintf("Reminder: you have a scheduled session at %s UTC with <@%d>.", whenStr, sch.RequesterID) if desc != "" { baseInvitee += "\nTopic: " + desc } if err := dmUser(s, sch.InviteeID, baseInvitee); err != nil { log.Printf("schedule: failed to DM invitee reminder: %v", err) } if err := svc.Schedule.MarkReminded(ctx, sch.ID); err != nil { log.Printf("schedule: failed to mark reminder sent for id=%d: %v", sch.ID, err) } } } }() } func dmUser(s *discordgo.Session, userID int64, content string) error { if s == nil || userID == 0 { return nil } uid := strconv.FormatInt(userID, 10) ch, err := s.UserChannelCreate(uid) if err != nil { return err } _, err = s.ChannelMessageSend(ch.ID, content) return err }