using Dalamud.Plugin.Services; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using QuestShare.Services.API; using System.Diagnostics; namespace QuestShare.Services { internal class ApiService : IService { private readonly string socketUrl = ConfigurationManager.Instance.ApiUrl; private HubConnection ApiConnection { get; set; } = null!; internal bool IsConnected => ApiConnection.State == HubConnectionState.Connected; internal HubConnectionState ConnectionState => ApiConnection.State; internal bool IsAuthorized { get; private set; } = false; private bool isDisposing = false; internal static string Token => ConfigurationManager.Instance.Token; private readonly List apiHandlers = []; public void Initialize() { var builder = new HubConnectionBuilder().WithUrl(socketUrl).ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Information).AddConsole(); }); ApiConnection = builder.Build(); ApiConnection.Closed += async (error) => { if (isDisposing) return; Log.Warning($"Connection closed... {error}"); Log.Warning($"Connection closed, retrying... {error}"); await Task.Delay(new Random().Next(0, 5) * 1000); await ApiConnection.StartAsync(); }; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously ApiConnection.Reconnected += async (error) => { Log.Information("Connection reconnected"); }; #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously ApiConnection.On(nameof(AuthRequest), AuthRequest_Client.HandleResponse); ApiConnection.On(nameof(Authorize), Authorize_Client.HandleResponse); ApiConnection.On(nameof(Register), Register_Client.HandleResponse); ApiConnection.On(nameof(GroupJoin), GroupJoin_Client.HandleResponse); ApiConnection.On(nameof(GroupLeave), GroupLeave_Client.HandleResponse); ApiConnection.On(nameof(Cancel), Cancel_Client.HandleResponse); ApiConnection.On(nameof(Update), Update_Client.HandleResponse); ApiConnection.On(nameof(Update.UpdateBroadcast), UpdateBroadcast_Client.HandleResponse); ApiConnection.On(nameof(Cancel.CancelBroadcast), Cancel_Client.HandleBroadcast); ApiConnection.On(nameof(GroupJoin.GroupJoinBroadcast), GroupJoin_Client.HandleBroadcast); ApiConnection.On(nameof(GroupLeave.GroupLeaveBroadcast), GroupLeave_Client.HandleBroadcast); ApiConnection.On(nameof(SessionStart), SessionStart_Client.HandleResponse); ClientState.Login += OnLogin; ClientState.Logout += OnLogout; Framework.Update += SavePersistedConfig; } public void Shutdown() { isDisposing = true; if (IsConnected) { ApiConnection.StopAsync(); } ApiConnection.DisposeAsync(); ClientState.Login -= OnLogin; ClientState.Logout -= OnLogout; Framework.Update -= SavePersistedConfig; } public void OnLogin() { if (Token != "" && ConfigurationManager.Instance.ConnectOnStartup) { Task.Run(Connect); } } public void OnLogout(int code, int type) { ConfigurationManager.Save(); IsAuthorized = false; ApiConnection.StopAsync().ConfigureAwait(false); } private void SavePersistedConfig(IFramework _) { ConfigurationManager.Instance.Token = Token; } public async Task Connect() { try { if (IsConnected) await ApiConnection.StopAsync(); isDisposing = false; await ApiConnection.StartAsync().ContinueWith(task => { if (task.IsFaulted) { Log.Error("Failed to connect to socket server"); } else { Log.Information("Connected to socket server"); } }); } catch (Exception ex) { Log.Error(ex.Message); } } public async Task Disconnect() { isDisposing = true; await ApiConnection.StopAsync(); } internal async Task Invoke(string methodName, object request) { if (!IsConnected) await Connect(); Log.Debug($"Invoking {methodName} with {JsonConvert.SerializeObject(request)}"); var s = new StackTrace(); Log.Debug(s.ToString()); await ApiConnection.InvokeAsync(methodName, request).ContinueWith(t => { if (t.IsFaulted) { Log.Error($"Failed to invoke {methodName}: {t.Exception}"); } }); } public static void DispatchAuthorize() => Authorize_Client.HandleDispatch(); public static void DispatchRegister() => Register_Client.HandleDispatch(); public static void DispatchGroup(Objects.ShareCode shareCode) => GroupJoin_Client.HandleDispatch(shareCode); public static void DispatchUngroup(Objects.Session session) => GroupLeave_Client.HandleDispatch(session); public static void DispatchCancel() => Cancel_Client.HandleDispatch(); public static void DispatchUpdate(Objects.OwnedSession session, List partyMembers) => Update_Client.HandleDispatch(session, partyMembers, true); public static void DispatchConfigChange(Objects.OwnedSession session, List partyMembers) => Update_Client.HandleDispatch(session, partyMembers, false); public static void DispatchSessionStart(Objects.OwnedSession session) => SessionStart_Client.HandleDispatch(session); public event EventHandler? GroupJoined; internal void OnGroupJoin(GroupJoinEventArgs e) { GroupJoined?.Invoke(this, e); } public class GroupJoinEventArgs : EventArgs { public bool IsSuccess { get; set; } public Objects.Session? Session { get; set; } } } }