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)