package events import ( "context" "log" "strconv" "time" "velox-bot/internal/db/services" "github.com/bwmarrin/discordgo" ) // StartTwitchLiveLoop periodically checks configured Twitch streamers and // sends live notifications to the configured channel. func StartTwitchLiveLoop(s *discordgo.Session, svc *services.Services) { if svc == nil || svc.Twitch == nil || s == nil { return } const ( interval = 30 * time.Second initialDelay = 10 * time.Second ) go func() { time.Sleep(initialDelay) log.Printf("twitch: starting live notification loop (interval=%s)", interval) ticker := time.NewTicker(interval) defer ticker.Stop() for range ticker.C { ctx := context.Background() guildIDs, err := svc.Twitch.ListGuilds(ctx) if err != nil { log.Printf("twitch: failed to list guilds: %v", err) continue } if len(guildIDs) == 0 { log.Printf("twitch: no guilds with configured streamers") } for _, guildID := range guildIDs { log.Printf("twitch: processing guild %d", guildID) channelID, ok, err := svc.Twitch.GetNotificationChannel(ctx, guildID) if err != nil { log.Printf("twitch: failed to get channel for guild %d: %v", guildID, err) continue } if !ok || channelID == 0 { log.Printf("twitch: no notification channel configured for guild %d", guildID) continue } users, err := svc.Twitch.ListUsersForGuild(ctx, guildID) if err != nil { log.Printf("twitch: failed to list users for guild %d: %v", guildID, err) continue } if len(users) == 0 { log.Printf("twitch: no streamers configured for guild %d", guildID) continue } chIDStr := strconv.FormatInt(channelID, 10) for _, username := range users { log.Printf("twitch: checking live status for guild %d, user %s", guildID, username) isLive, err := svc.Twitch.IsUserStreaming(ctx, username) if err != nil { log.Printf("twitch: failed to check stream for %s: %v", username, err) continue } status, ok, err := svc.Twitch.GetStatus(ctx, guildID, username) if err != nil { log.Printf("twitch: failed to get status for %s: %v", username, err) continue } if !ok { status = "not live" } if isLive { if status == "not live" { log.Printf("twitch: %s went live in guild %d, sending notification to channel %s", username, guildID, chIDStr) liveURL := "https://www.twitch.tv/" + username content := "@everyone" previewURL := "https://static-cdn.jtvnw.net/previews-ttv/live_user_" + username + "-640x360.jpg" embed := &discordgo.MessageEmbed{ Title: ":red_circle: " + username + " is now LIVE on Twitch!", Description: "Click the link below to watch the stream.", URL: liveURL, Color: 0x9146FF, // Twitch purple Image: &discordgo.MessageEmbedImage{ URL: previewURL, }, } if _, err := s.ChannelMessageSendComplex(chIDStr, &discordgo.MessageSend{ Content: content, Embed: embed, }); err != nil { log.Printf("twitch: failed to send notification for %s: %v", username, err) continue } if err := svc.Twitch.UpdateStatus(ctx, guildID, username, "live"); err != nil { log.Printf("twitch: failed to update status to live for %s in guild %d: %v", username, guildID, err) } } } else { if status != "not live" { log.Printf("twitch: %s is no longer live in guild %d, updating status", username, guildID) if err := svc.Twitch.UpdateStatus(ctx, guildID, username, "not live"); err != nil { log.Printf("twitch: failed to update status to not live for %s in guild %d: %v", username, guildID, err) } } } } } } }() }