211 lines
8.0 KiB
C#
211 lines
8.0 KiB
C#
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<string> 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));
|
|
}
|
|
}
|
|
}
|