From ba683ad1f99db59e46035f32092607c29b9cb0fd Mon Sep 17 00:00:00 2001 From: Li Date: Fri, 15 Mar 2024 19:08:47 +1300 Subject: [PATCH] Add discord client --- PluralRichPresnce/Api/AWebSocket.cs | 136 ++++++++++++ .../{SimplyPlural => Api}/ApiType.cs | 2 +- .../Api/DataReceivedEventArgs.cs | 17 ++ .../Api/TextReceivedEventArgs.cs | 17 ++ PluralRichPresnce/Discord/DiscordClient.cs | 210 ++++++++++++++++++ PluralRichPresnce/Discord/DiscordRpc.cs | 39 ++++ .../Discord/IDiscordActivitySetter.cs | 13 ++ PluralRichPresnce/Discord/SystemMember.cs | 25 +++ PluralRichPresnce/DiscordRPC.cs | 43 ---- PluralRichPresnce/Program.cs | 23 +- .../PublishProfiles/FolderProfile.pubxml | 5 +- .../PublishProfiles/FolderProfile.pubxml.user | 2 +- PluralRichPresnce/SimplyPlural/Rest.cs | 1 + PluralRichPresnce/SimplyPlural/Socket.cs | 119 ++++------ 14 files changed, 516 insertions(+), 136 deletions(-) create mode 100644 PluralRichPresnce/Api/AWebSocket.cs rename PluralRichPresnce/{SimplyPlural => Api}/ApiType.cs (78%) create mode 100644 PluralRichPresnce/Api/DataReceivedEventArgs.cs create mode 100644 PluralRichPresnce/Api/TextReceivedEventArgs.cs create mode 100644 PluralRichPresnce/Discord/DiscordClient.cs create mode 100644 PluralRichPresnce/Discord/DiscordRpc.cs create mode 100644 PluralRichPresnce/Discord/IDiscordActivitySetter.cs create mode 100644 PluralRichPresnce/Discord/SystemMember.cs delete mode 100644 PluralRichPresnce/DiscordRPC.cs diff --git a/PluralRichPresnce/Api/AWebSocket.cs b/PluralRichPresnce/Api/AWebSocket.cs new file mode 100644 index 0000000..dfbd932 --- /dev/null +++ b/PluralRichPresnce/Api/AWebSocket.cs @@ -0,0 +1,136 @@ +using Newtonsoft.Json; +using PluralRichPresence.Api; +using System.Dynamic; +using System.Net.WebSockets; +using System.Text; + +namespace PluralRichPresence.Api +{ + public class AWebSocket : IDisposable + { + private string websocketServerUri; + SemaphoreSlim? semaphore = new SemaphoreSlim(1, 1); + ClientWebSocket? wss = null; + + public event EventHandler? DataReceived; + public event EventHandler? TextReceived; + public event EventHandler? Disconnected; + + private void onTextReceived(string receivedString) + { + if (TextReceived is not null) + { + TextReceived(this, new TextReceivedEventArgs(receivedString)); + } + } + + private void onDataReceived(byte[] receivedData) + { + if (DataReceived is not null) + { + DataReceived(this, new DataReceivedEventArgs(receivedData)); + } + } + + private void onDisconnect() + { + if (Disconnected is not null) + { + if (semaphore is null || !semaphore.Wait(0)) return; + Disconnected(this, new EventArgs()); + } + } + + public AWebSocket(string serverUri) + { + this.websocketServerUri = serverUri; + } + + private async Task receiveMessageText() + { + byte[] byteArray = await receiveMessageBytes(); + return Encoding.UTF8.GetString(byteArray); + } + + private async Task receiveMessageBytes() + { + List totalPayload = new List(); + byte[] buffer = new byte[0x8000]; + + while (wss is not null && wss.State == WebSocketState.Open) + { + try + { + WebSocketReceiveResult? result = await wss.ReceiveAsync(buffer, CancellationToken.None); + + for (int i = 0; i < result.Count; i++) + totalPayload.Add(buffer[i]); + + if (result.EndOfMessage) + return totalPayload.ToArray(); + } + catch(Exception) { onDisconnect(); break; }; + } + + return totalPayload.ToArray(); + } + + + private async Task receiveTask() + { + while (wss is not null && wss.State == WebSocketState.Open) { + try { + string message = await receiveMessageText(); + Logger.Debug("< " + message); + onTextReceived(message); + + } + catch (Exception) { Logger.Debug("failed"); break; }; + } + onDisconnect(); + } + + public async Task Connect() + { + wss = new ClientWebSocket(); + await wss.ConnectAsync(new Uri(this.websocketServerUri), CancellationToken.None); + _ = Task.Run(() => receiveTask()); + try { + if(semaphore is not null) + semaphore.Release(); + } + catch (SemaphoreFullException) { }; + } + + public async Task SendText(string text) + { + Logger.Debug("> "+text); + try { + if (wss is not null) + await wss.SendAsync(Encoding.UTF8.GetBytes(text), WebSocketMessageType.Text, true, CancellationToken.None); + } + catch (Exception) { onDisconnect(); } + } + + public async Task Close() + { + if (wss is not null) + await wss.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); + } + + public void Dispose() + { + if(wss is not null) + { + this.Close().Wait(); + wss.Dispose(); + wss = null; + } + if (semaphore is not null) + { + semaphore.Dispose(); + semaphore = null; + } + } + } +} diff --git a/PluralRichPresnce/SimplyPlural/ApiType.cs b/PluralRichPresnce/Api/ApiType.cs similarity index 78% rename from PluralRichPresnce/SimplyPlural/ApiType.cs rename to PluralRichPresnce/Api/ApiType.cs index b1db0af..779b3d9 100644 --- a/PluralRichPresnce/SimplyPlural/ApiType.cs +++ b/PluralRichPresnce/Api/ApiType.cs @@ -1,4 +1,4 @@ -namespace PluralRichPresence.SimplyPlural +namespace PluralRichPresence.Api { public class ApiType { diff --git a/PluralRichPresnce/Api/DataReceivedEventArgs.cs b/PluralRichPresnce/Api/DataReceivedEventArgs.cs new file mode 100644 index 0000000..a6ee499 --- /dev/null +++ b/PluralRichPresnce/Api/DataReceivedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PluralRichPresence.Api +{ + public class DataReceivedEventArgs : EventArgs + { + public byte[] Data; + public DataReceivedEventArgs(byte[] data) + { + this.Data = data; + } + } +} diff --git a/PluralRichPresnce/Api/TextReceivedEventArgs.cs b/PluralRichPresnce/Api/TextReceivedEventArgs.cs new file mode 100644 index 0000000..cfd9d67 --- /dev/null +++ b/PluralRichPresnce/Api/TextReceivedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PluralRichPresence.Api +{ + public class TextReceivedEventArgs : EventArgs + { + public string Text; + public TextReceivedEventArgs(string text) + { + this.Text = text; + } + } +} diff --git a/PluralRichPresnce/Discord/DiscordClient.cs b/PluralRichPresnce/Discord/DiscordClient.cs new file mode 100644 index 0000000..e486122 --- /dev/null +++ b/PluralRichPresnce/Discord/DiscordClient.cs @@ -0,0 +1,210 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PluralRichPresence.Api; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace PluralRichPresence.Discord +{ + public class DiscordClient : ApiType, IDiscordActivitySetter + { + const string DEFAULT_DISCORD_GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json"; + private const string DEFAULT_APPLICATION_ID = "1163661006719963158"; + private static HttpClient client = new HttpClient(); + + private SystemMember? lastFronter = null; + private int? seq = null; + private Timer? keepAliveTimer = null; + private AWebSocket? wSock = null; + private ManualResetEvent waitForConnect = new ManualResetEvent(false); + private int heartBeatInterval = 0; + + private void wSockTextReceived(object? sender, TextReceivedEventArgs e) + { + try + { + if (e.Text == "") return; + dynamic? jsonData = JsonConvert.DeserializeObject(e.Text); + if (jsonData is null) return; + seq = jsonData.s; + switch ((int)jsonData.op) + { + case 0: + if (jsonData.t == "READY") waitForConnect.Set(); + break; + case 1: + _ = sendKeepAlive(); + break; + case 9: + _ = reconnect(); + break; + case 10: + sendKeepAlive().Wait(); + heartBeatInterval = jsonData.d.heartbeat_interval; + keepAliveTimer = new Timer((TimerCallback) => { _ = sendKeepAlive(); }, null, heartBeatInterval, 0); + break; + + } + } + catch (Exception) { }; + } + + private void wSockDisconnected(object? sender, EventArgs e) + { + + _ = reconnect(); + } + + private async Task getExternalAsset(string url) + { + try + { + dynamic extAssReq = new JObject(); + extAssReq.urls = new JArray(url); + + StringContent apiReq = new StringContent(JsonConvert.SerializeObject(extAssReq)); + apiReq.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + + HttpResponseMessage resp = await client.PostAsync("https://discord.com/api/v9/applications/" + Config.GetEntry("DISCORD_APPLICATION_ID", DEFAULT_APPLICATION_ID) + "/external-assets", apiReq); + resp.EnsureSuccessStatusCode(); + string responseString = await resp.Content.ReadAsStringAsync(); + dynamic? jsonResponse = JsonConvert.DeserializeObject(responseString); + if(jsonResponse is not null) + return jsonResponse[0].external_asset_path; + + return "1163671691000557591"; + } + catch (Exception) { return "1163671691000557591"; }; + } + + private async Task connect() + { + wSock = new AWebSocket(Config.GetEntry("DISCORD_GATEWAY_WEBSOCKET_URL", DEFAULT_DISCORD_GATEWAY_URL)); + await wSock.Connect(); + wSock.TextReceived += wSockTextReceived; + wSock.Disconnected += wSockDisconnected; + } + private async Task reconnect() + { + waitForConnect.Reset(); + + while (true) + { + try + { + try + { + if (wSock is not null) wSock.Dispose(); + wSock = null; + } + catch (Exception) { }; + await connect(); + await sendLogin(); + + if (lastFronter is not null) + SetFronter(lastFronter); + } + catch (Exception) { Logger.Debug("failed to connect."); continue; } + break; + } + } + + public DiscordClient(string token) : base(token) + { + client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9036 Chrome/108.0.5359.215 Electron/22.3.26 Safari/537.36"); + client.DefaultRequestHeaders.TryAddWithoutValidation("Origin", "https://discord.com/"); + client.DefaultRequestHeaders.TryAddWithoutValidation("Referer", "https://discord.com/"); + client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", this.token); + + _ = reconnect(); + } + + private async Task sendLogin() + { + dynamic gatewayOp = new JObject(); + gatewayOp.op = 2; + gatewayOp.d = new JObject(); + gatewayOp.d.token = this.token; + gatewayOp.d.capabilities = 16381; + + gatewayOp.d.properties = new JObject(); + gatewayOp.d.properties.os = "Windows"; + gatewayOp.d.properties.browser = "Discord Client"; + gatewayOp.d.properties.release_channel = "stable"; + gatewayOp.d.properties.client_version = "1.0.9035"; + gatewayOp.d.properties.os_version = "10.0.19045"; + gatewayOp.d.properties.os_arch = "x64"; + gatewayOp.d.properties.app_arch = "ia32"; + gatewayOp.d.properties.system_locale = "en-G"; + gatewayOp.d.properties.browser_user_agent = "ia32"; + gatewayOp.d.properties.browser_version = "22.3.26"; + gatewayOp.d.properties.client_build_number = 274388; + gatewayOp.d.properties.native_build_number = 44780; + gatewayOp.d.properties.client_event_source = null; + + if (wSock is not null) + await wSock.SendText(JsonConvert.SerializeObject(gatewayOp)); + } + private async Task sendKeepAlive() + { + dynamic gatewayOp = new JObject(); + gatewayOp.op = 1; + gatewayOp.d = seq; + + Logger.Debug("SENDING KEEP ALIVE MESSAGE"); + + if (wSock is not null) + await wSock.SendText(JsonConvert.SerializeObject(gatewayOp)); + + if(keepAliveTimer is not null) + keepAliveTimer.Change(this.heartBeatInterval, 0); + } + + public void SetFronter(SystemMember sysMember) + { + waitForConnect.WaitOne(); + + dynamic gatewayOp = new JObject(); + gatewayOp.op = 3; + + gatewayOp.d = new JObject(); + gatewayOp.d.status = "online"; + gatewayOp.d.since = 0; + + dynamic fronterActivity = new JObject(); + fronterActivity.state = sysMember.Pronouns; + fronterActivity.details = sysMember.Name; + + if(sysMember.TimeStamp is not null) + { + fronterActivity.timestamps = new JObject(); + fronterActivity.timestamps.start = sysMember.TimeStamp; + } + + fronterActivity.assets = new JObject(); + fronterActivity.assets.large_image = (sysMember.ProfilePhotoUrl is not null) ? "mp:"+getExternalAsset(sysMember.ProfilePhotoUrl).Result : "1163671691000557591"; + fronterActivity.assets.large_text = sysMember.Name + " - " + sysMember.Pronouns; + + if (sysMember.ProfilePhotoUrl is not null) + fronterActivity.assets.small_image = "1163671691000557591"; + + fronterActivity.name = "Currently Fronting: " + sysMember.Name; + fronterActivity.application_id = Config.GetEntry("DISCORD_APPLICATION_ID", DEFAULT_APPLICATION_ID); + fronterActivity.type = 0; + + gatewayOp.d.activities = new JArray(fronterActivity); + gatewayOp.d.afk = false; + gatewayOp.d.broadcast = null; + + if(wSock is not null) + _ = wSock.SendText(JsonConvert.SerializeObject(gatewayOp)); + } + } +} diff --git a/PluralRichPresnce/Discord/DiscordRpc.cs b/PluralRichPresnce/Discord/DiscordRpc.cs new file mode 100644 index 0000000..ab3366f --- /dev/null +++ b/PluralRichPresnce/Discord/DiscordRpc.cs @@ -0,0 +1,39 @@ +using DiscordRPC; +using DiscordRPC.Logging; + +namespace PluralRichPresence.Discord +{ + public class DiscordRpc : IDiscordActivitySetter + { + private const string DEFAULT_APPLICATION_ID = "1163661006719963158"; + private const string DEFAULT_ICON = "plural"; + private DiscordRpcClient client; + public DiscordRpc() + { + client = new DiscordRpcClient(Config.GetEntry("DISCORD_APPLICATION_ID", DEFAULT_APPLICATION_ID)); + client.Logger = new ConsoleLogger() { Level = LogLevel.None }; + client.OnReady += (sender, e) => { }; + client.OnPresenceUpdate += (sender, e) => { }; + client.Initialize(); + } + + public void SetFronter(SystemMember sysMember) + { + client.SetPresence(new RichPresence() + { + Details = sysMember.Name, + State = sysMember.Pronouns, + Assets = new Assets() + { + LargeImageKey = (sysMember.ProfilePhotoUrl is not null) ? sysMember.ProfilePhotoUrl : Config.GetEntry("PLURAL_ICON_NAME", DEFAULT_ICON), + LargeImageText = sysMember.Name + " - " + sysMember.Pronouns, + SmallImageKey = Config.GetEntry("PLURAL_ICON_NAME", DEFAULT_ICON) + }, + Timestamps = new Timestamps() + { + StartUnixMilliseconds = sysMember.TimeStamp + } + }); + } + } +} diff --git a/PluralRichPresnce/Discord/IDiscordActivitySetter.cs b/PluralRichPresnce/Discord/IDiscordActivitySetter.cs new file mode 100644 index 0000000..424b33c --- /dev/null +++ b/PluralRichPresnce/Discord/IDiscordActivitySetter.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PluralRichPresence.Discord +{ + public interface IDiscordActivitySetter + { + public void SetFronter(SystemMember sysMember); + } +} diff --git a/PluralRichPresnce/Discord/SystemMember.cs b/PluralRichPresnce/Discord/SystemMember.cs new file mode 100644 index 0000000..8168f47 --- /dev/null +++ b/PluralRichPresnce/Discord/SystemMember.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PluralRichPresence.Discord +{ + public class SystemMember + { + public string Name; + public string Pronouns; + public string? ProfilePhotoUrl; + public ulong? TimeStamp; + + + public SystemMember(string name, string pronouns, string? profilePhotoUrl, ulong? timeStamp) + { + this.Name = name; + this.Pronouns = pronouns; + this.ProfilePhotoUrl = profilePhotoUrl; + this.TimeStamp = timeStamp; + } + } +} diff --git a/PluralRichPresnce/DiscordRPC.cs b/PluralRichPresnce/DiscordRPC.cs deleted file mode 100644 index 646d47d..0000000 --- a/PluralRichPresnce/DiscordRPC.cs +++ /dev/null @@ -1,43 +0,0 @@ -using DiscordRPC; -using DiscordRPC.Logging; - -namespace PluralRichPresence -{ - - public class DiscordRPC - { - private DiscordRpcClient client; - private const string DEFAULT_APPLICATION_ID = "1163661006719963158"; - public DiscordRPC() - { - client = new DiscordRpcClient(Config.GetEntry("DISCORD_APPLICATION_ID", DEFAULT_APPLICATION_ID)); - - client.Logger = new ConsoleLogger() { Level = LogLevel.None }; - - client.OnReady += (sender, e) => {}; - - client.OnPresenceUpdate += (sender, e) => {}; - - client.Initialize(); - } - - public void SetFronter(string user, string pronouns, string profile, ulong? timeStamp) - { - client.SetPresence(new RichPresence() - { - Details = user, - State = pronouns, - Assets = new Assets() - { - LargeImageKey = profile, - LargeImageText = user + " - " + pronouns, - SmallImageKey = "plural" - }, - Timestamps = new Timestamps() - { - StartUnixMilliseconds = timeStamp - } - }); - } - } -} diff --git a/PluralRichPresnce/Program.cs b/PluralRichPresnce/Program.cs index 018efc8..94a9417 100644 --- a/PluralRichPresnce/Program.cs +++ b/PluralRichPresnce/Program.cs @@ -1,5 +1,6 @@ using PluralRichPresence.Avatar; using PluralRichPresence.SimplyPlural; +using PluralRichPresence.Discord; using System.Drawing; using System.Drawing.Imaging; using System.Linq; @@ -10,7 +11,7 @@ namespace PluralRichPresence internal class Program { private static ManualResetEvent waitHandle = new ManualResetEvent(false); - private static DiscordRPC discordRpc = new DiscordRPC(); + private static IDiscordActivitySetter? discordSetter = null; private static System? system; private const string KEY_SIMPLY_PLURAL_TOKEN = "SIMPLY_PLURAL_TOKEN"; @@ -72,21 +73,27 @@ namespace PluralRichPresence if (frontingMembers.Length > 0) { Console.Write("\r" + FmtFronterNames(frontingMembers) + " is fronting!"); - discordRpc.SetFronter(FmtFronterNames(frontingMembers), - FmtFronterPronouns(frontingMembers), - FmtAvatar(frontingMembers), - frontingMembers.OrderByDescending(o => o.FrontStartTime).First().FrontStartTime); + if (discordSetter is null) return; + + discordSetter.SetFronter(new SystemMember(FmtFronterNames(frontingMembers), + FmtFronterPronouns(frontingMembers), + FmtAvatar(frontingMembers), + frontingMembers.OrderByDescending(o => o.FrontStartTime).First().FrontStartTime)); } else { Console.Write("\r No one is fronting!"); - discordRpc.SetFronter("No one is fronting.", "This doesn't make much sense?", "plural", null); + if (discordSetter is null) return; + + discordSetter.SetFronter(new SystemMember("No one is fronting.", "This doesn't make much sense?", null, null)); } } public static async Task Main(string[] args) { - - if(!Config.EntryExists(KEY_SIMPLY_PLURAL_TOKEN)) + discordSetter = new DiscordClient(Config.GetEntry("DISCORD_TOKEN")); + //discordSetter = new DiscordRpc(); + + if (!Config.EntryExists(KEY_SIMPLY_PLURAL_TOKEN)) { Console.Write("Enter Simply Plural API Key: "); string? token = Console.ReadLine(); diff --git a/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml b/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml index 95f00a3..9137e7d 100644 --- a/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml +++ b/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml @@ -6,14 +6,13 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release Any CPU - bin\Release\net8.0\publish\win-x86\ + bin\Release\net8.0\publish\linux-x64\ FileSystem <_TargetId>Folder net8.0 - win-x86 + linux-x64 true true - false false \ No newline at end of file diff --git a/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml.user b/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml.user index 76af2be..7e82b4e 100644 --- a/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/PluralRichPresnce/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - True|2023-12-28T10:24:43.7413097Z;True|2023-12-28T23:24:32.3866287+13:00;True|2023-12-28T23:22:28.7251892+13:00;True|2023-12-28T21:45:45.2231538+13:00;True|2023-12-28T21:00:25.2444119+13:00;True|2023-12-28T20:56:12.0172711+13:00;True|2023-12-28T20:54:22.1893325+13:00;True|2023-12-28T20:53:03.6353258+13:00;True|2023-11-19T17:13:26.6652642+13:00;True|2023-11-05T19:18:40.8205304+13:00;True|2023-11-05T19:17:01.7660736+13:00;True|2023-11-05T19:11:32.6382930+13:00;True|2023-11-05T19:11:09.6820258+13:00;False|2023-11-05T19:10:42.2427675+13:00;True|2023-10-18T14:13:41.8203499+13:00;True|2023-10-18T14:05:36.8381808+13:00;True|2023-10-17T21:30:38.9682339+13:00; + True|2024-03-15T06:01:46.3092686Z;True|2023-12-28T23:24:43.7413097+13:00;True|2023-12-28T23:24:32.3866287+13:00;True|2023-12-28T23:22:28.7251892+13:00;True|2023-12-28T21:45:45.2231538+13:00;True|2023-12-28T21:00:25.2444119+13:00;True|2023-12-28T20:56:12.0172711+13:00;True|2023-12-28T20:54:22.1893325+13:00;True|2023-12-28T20:53:03.6353258+13:00;True|2023-11-19T17:13:26.6652642+13:00;True|2023-11-05T19:18:40.8205304+13:00;True|2023-11-05T19:17:01.7660736+13:00;True|2023-11-05T19:11:32.6382930+13:00;True|2023-11-05T19:11:09.6820258+13:00;False|2023-11-05T19:10:42.2427675+13:00;True|2023-10-18T14:13:41.8203499+13:00;True|2023-10-18T14:05:36.8381808+13:00;True|2023-10-17T21:30:38.9682339+13:00; \ No newline at end of file diff --git a/PluralRichPresnce/SimplyPlural/Rest.cs b/PluralRichPresnce/SimplyPlural/Rest.cs index ace275c..55bea5b 100644 --- a/PluralRichPresnce/SimplyPlural/Rest.cs +++ b/PluralRichPresnce/SimplyPlural/Rest.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json.Linq; using System.Net.Http.Headers; using System; using System.Text; +using PluralRichPresence.Api; namespace PluralRichPresence.SimplyPlural { diff --git a/PluralRichPresnce/SimplyPlural/Socket.cs b/PluralRichPresnce/SimplyPlural/Socket.cs index 92129ef..d4639af 100644 --- a/PluralRichPresnce/SimplyPlural/Socket.cs +++ b/PluralRichPresnce/SimplyPlural/Socket.cs @@ -1,17 +1,16 @@ using Newtonsoft.Json; +using PluralRichPresence.Api; using System.Dynamic; -using System.Net.WebSockets; using System.Text; namespace PluralRichPresence.SimplyPlural { public class Socket : ApiType { + private AWebSocket? wSock; public const int KEEPALIVE_INTERVAL = 10 * 1000; public const string DEFAULT_WEBSOCKET_SERVER_URI = "wss://api.apparyllis.com/v1/socket"; public event EventHandler? FronterChanged; - SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); - ClientWebSocket? wss = null; Timer? keepAliveTimer = null; @@ -22,34 +21,6 @@ namespace PluralRichPresence.SimplyPlural FronterChanged(this, new EventArgs()); } } - private async Task receiveMessageText() - { - byte[] byteArray = await receiveMessageBytes(); - return Encoding.UTF8.GetString(byteArray); - } - private async Task receiveMessageBytes() - { - List totalPayload = new List(); - byte[] buffer = new byte[0x8000]; - - while (wss is not null && wss.State == WebSocketState.Open) - { - try - { - WebSocketReceiveResult? result = await wss.ReceiveAsync(buffer, CancellationToken.None); - - for (int i = 0; i < result.Count; i++) - totalPayload.Add(buffer[i]); - - if (result.EndOfMessage) - return totalPayload.ToArray(); - } - catch(Exception) { await reconnect(); break; }; - } - - return totalPayload.ToArray(); - } - private void doUpdate(dynamic jsonData) { string target = jsonData.target; @@ -61,40 +32,8 @@ namespace PluralRichPresence.SimplyPlural } } - private async Task receiveTask() - { - while (wss is not null && wss.State == WebSocketState.Open) - { - try - { - string message = await receiveMessageText(); - Logger.Debug("< " + message); - if (message == "pong") continue; - - try - { - dynamic? jsonData = JsonConvert.DeserializeObject(message); - if (jsonData is null) continue; - string? type = jsonData.msg; - switch (type) - { - case "update": - doUpdate(jsonData); - break; - case null: - default: - break; - - } - } - catch (Exception) { }; - } - catch (Exception) { Logger.Debug("failed"); break; }; - } - } private async Task reconnect() { - if (!await semaphore.WaitAsync(0)) return; while (true) { @@ -102,45 +41,64 @@ namespace PluralRichPresence.SimplyPlural { try { - if (wss is not null) await wss.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); - wss = null; + if (wSock is not null) wSock.Dispose(); + wSock = null; } catch (Exception) { }; await connect(); await sendLogin(); - _ = Task.Run(() => receiveTask()); this.keepAliveTimer = new Timer((TimerCallback) => { _ = sendKeepAlive(); }, null, KEEPALIVE_INTERVAL, 0); } catch (Exception) { Logger.Debug("failed to connect."); continue; } break; } - try - { - semaphore.Release(); - } - catch (SemaphoreFullException) { }; - } private async Task connect() { - wss = new ClientWebSocket(); - await wss.ConnectAsync(new Uri(Config.GetEntry("SIMPLY_PLURAL_WEBSOCKET_URI", DEFAULT_WEBSOCKET_SERVER_URI)), CancellationToken.None); + wSock = new AWebSocket(Config.GetEntry("SIMPLY_PLURAL_WEBSOCKET_URI", DEFAULT_WEBSOCKET_SERVER_URI)); + await wSock.Connect(); + wSock.TextReceived += wSockTextReceived; + wSock.Disconnected += wSockDisconnected; } - private async Task sendText(string text) + private void wSockTextReceived(object? sender, TextReceivedEventArgs e) { - Logger.Debug("> "+text); + string message = e.Text; + if (message == "pong") return; + try { - await wss.SendAsync(Encoding.UTF8.GetBytes(text), WebSocketMessageType.Text, true, CancellationToken.None); + dynamic? jsonData = JsonConvert.DeserializeObject(message); + if (jsonData is null) return; + string? type = jsonData.msg; + switch (type) + { + case "update": + doUpdate(jsonData); + break; + case null: + default: + break; + + } } - catch (Exception) { await reconnect(); } + catch (Exception) { }; } + + private void wSockDisconnected(object? sender, EventArgs e) + { + _ = reconnect(); + } + + private async Task sendKeepAlive() { - await sendText("ping"); + if (wSock is not null) { + await wSock.SendText("ping"); + } + if (keepAliveTimer is not null) keepAliveTimer.Change(KEEPALIVE_INTERVAL, 0); } @@ -150,7 +108,8 @@ namespace PluralRichPresence.SimplyPlural data.op = "authenticate"; data.token = token; string authenticateJson = JsonConvert.SerializeObject(data); - await sendText(authenticateJson); + if(wSock is not null) + await wSock.SendText(authenticateJson); } public Socket(string token) : base(token)