using System; using System.Linq; using System.Timers; namespace MosswartMassacre { /// /// Streams high-priority quest timer data via WebSocket on a 30-second interval. /// internal class QuestStreamingService { private readonly IPluginLogger _logger; private Timer _timer; internal QuestStreamingService(IPluginLogger logger) { _logger = logger; } internal void Start() { _timer = new Timer(Constants.QuestStreamingIntervalMs); _timer.Elapsed += OnTimerElapsed; _timer.AutoReset = true; _timer.Start(); } internal void Stop() { if (_timer != null) { _timer.Stop(); _timer.Elapsed -= OnTimerElapsed; _timer.Dispose(); _timer = null; } } internal bool IsRunning => _timer != null && _timer.Enabled; private void OnTimerElapsed(object sender, ElapsedEventArgs e) { try { if (PluginSettings.Instance?.VerboseLogging == true) { _logger?.Log("[QUEST-STREAM] Timer fired, checking conditions..."); } if (!PluginCore.WebSocketEnabled) { if (PluginSettings.Instance?.VerboseLogging == true) { _logger?.Log("[QUEST-STREAM] WebSocket not enabled, skipping"); } return; } var questManager = PluginCore.questManager; if (questManager?.QuestList == null || questManager.QuestList.Count == 0) { if (PluginSettings.Instance?.VerboseLogging == true) { _logger?.Log($"[QUEST-STREAM] No quest data available (null: {questManager?.QuestList == null}, count: {questManager?.QuestList?.Count ?? 0})"); } return; } var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var priorityQuests = questManager.QuestList .Where(q => IsHighPriorityQuest(q.Id)) .GroupBy(q => q.Id) .Select(g => g.First()) .ToList(); if (PluginSettings.Instance?.VerboseLogging == true) { _logger?.Log($"[QUEST-STREAM] Found {priorityQuests.Count} priority quests to stream"); } foreach (var quest in priorityQuests) { try { string questName = questManager.GetFriendlyQuestName(quest.Id); long timeRemaining = quest.ExpireTime - currentTime; string countdown = FormatCountdown(timeRemaining); if (PluginSettings.Instance?.VerboseLogging == true) { _logger?.Log($"[QUEST-STREAM] Sending: {questName} - {countdown}"); } System.Threading.Tasks.Task.Run(() => WebSocket.SendQuestDataAsync(questName, countdown)); } catch (Exception ex) { _logger?.Log($"[QUEST-STREAM] Error streaming quest {quest.Id}: {ex.Message}"); } } } catch (Exception ex) { _logger?.Log($"[QUEST-STREAM] Error in timer handler: {ex.Message}"); } } internal static bool IsHighPriorityQuest(string questId) { return questId == "stipendtimer_0812" || questId == "augmentationblankgemacquired" || questId == "insatiableeaterjaw"; } internal static string FormatCountdown(long seconds) { if (seconds <= 0) return "READY"; var timeSpan = TimeSpan.FromSeconds(seconds); if (timeSpan.TotalDays >= 1) return $"{(int)timeSpan.TotalDays}d {timeSpan.Hours:D2}h"; else if (timeSpan.TotalHours >= 1) return $"{timeSpan.Hours}h {timeSpan.Minutes:D2}m"; else if (timeSpan.TotalMinutes >= 1) return $"{timeSpan.Minutes}m {timeSpan.Seconds:D2}s"; else return $"{timeSpan.Seconds}s"; } } }