This commit is contained in:
Li 2023-10-17 21:04:18 +13:00
parent 477e223e93
commit f67fdbe537
13 changed files with 519 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vs/*
*/bin/*
*/obj/*

25
PluralRichPresence.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralRichPresence", "PluralRichPresnce\PluralRichPresence.csproj", "{F0DA03B7-E669-48A4-85E5-3B3080552BFF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F0DA03B7-E669-48A4-85E5-3B3080552BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0DA03B7-E669-48A4-85E5-3B3080552BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0DA03B7-E669-48A4-85E5-3B3080552BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0DA03B7-E669-48A4-85E5-3B3080552BFF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2D6CBC9C-96A6-4B89-ABC7-46DA68F481B8}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence
{
public class ConfigReader
{
const string CFG_NAME = "plurality.cfg";
const char SEPERATOR = ':';
public static string ReadString(string key)
{
StreamReader txtReader = new StreamReader(File.OpenRead(CFG_NAME));
for (string? line = txtReader.ReadLine(); line is not null; line = txtReader.ReadLine())
{
line = line.Trim().ReplaceLineEndings(String.Empty);
if (!line.Contains(SEPERATOR)) continue;
string[] configOptions = line.Split(SEPERATOR);
if (configOptions[0].Trim() == key)
return configOptions[1].Trim();
}
return "";
}
}
}

View File

@ -0,0 +1,45 @@
using DiscordRPC;
using DiscordRPC.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence
{
public class DiscordRPC
{
private DiscordRpcClient client;
public DiscordRPC()
{
client = new DiscordRpcClient(ConfigReader.ReadString("APPLICATION_ID"));
client.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
client.OnReady += (sender, e) => {};
client.OnPresenceUpdate += (sender, e) => {};
client.Initialize();
}
public void SetFronter(string user, string pronouns, string profile)
{
client.SetPresence(new RichPresence()
{
Details = user,
State = pronouns,
Assets = new Assets()
{
LargeImageKey = profile,
LargeImageText = user + " - " +pronouns,
SmallImageKey = "plural"
}
});
}
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
using DiscordRPC;
using DiscordRPC.Logging;
using Newtonsoft.Json;
using PluralRichPresence.SimplyPlural;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace PluralRichPresence
{
internal class Program
{
private static ManualResetEvent waitHandle = new ManualResetEvent(false);
private static DiscordRPC discordRpc = new DiscordRPC();
public static System system;
public static async Task UpdateFronterStatus()
{
// get fronting members
Member[] frontingMembers = await system.GetCurrentFronterInfo();
if (frontingMembers.Length > 0)
{
Member fronter = frontingMembers.First();
discordRpc.SetFronter(fronter.Name, fronter.Pronouns, (fronter.AvatarURL is null) ? "plural" : fronter.AvatarURL);
}
else
{
discordRpc.SetFronter("No one is fronting.", "This doesn't make much sense?", "plural");
}
}
public static void Main(string[] args)
{
system = new System(ConfigReader.ReadString("SIMPLY_PLURAL_TOKEN"));
system.FronterChanged += onFronterChanged;
_ = UpdateFronterStatus();
waitHandle.WaitOne();
}
private static void onFronterChanged(object? sender, EventArgs e)
{
_ = UpdateFronterStatus();
}
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net7.0\publish\win-x86\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
</Project>

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence.SimplyPlural
{
public class ApiType
{
internal string token;
public ApiType(string token)
{
this.token = token;
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Net.WebRequestMethods;
namespace PluralRichPresence.SimplyPlural
{
public class Member
{
public string Name;
public string Pronouns;
public string? AvatarURL;
public string Id;
public string SystemId;
public Member(string systemId,
string name,
string pronouns,
string id,
string? avatarUUID,
string? avatarURL)
{
this.Name = name;
this.Pronouns = pronouns;
if ((avatarURL == "" || avatarURL is null) &&
(avatarUUID != "" || avatarUUID is not null))
{
this.AvatarURL = "https://spaces.apparyllis.com/avatars/" + systemId + "/" + avatarUUID;
}
this.Id = id;
this.SystemId = systemId;
}
}
}

View File

@ -0,0 +1,78 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence.SimplyPlural
{
public class Rest : ApiType
{
private HttpClient client = new HttpClient();
private const string HOST = "https://api.apparyllis.com";
private async Task<dynamic> get(string apiEndpoint)
{
HttpResponseMessage response = await client.GetAsync(HOST + apiEndpoint);
string responseText = Encoding.UTF8.GetString(await response.Content.ReadAsByteArrayAsync());
Console.WriteLine("HTTP: " + responseText);
dynamic? result = JsonConvert.DeserializeObject(responseText);
return (result is null) ? new JObject() : (dynamic)result;
}
public async Task<string> GetSystemId()
{
dynamic system = await get("/v1/me");
return system.id;
}
public async Task<string[]> GetCurrentFronters()
{
dynamic fronters = await get("/v1/fronters");
List<string> userIdList = new List<string>();
for (int i = 0; i < fronters.Count; i++)
{
bool fronterExist = fronters[i].exists;
if (!fronterExist) continue;
string fronterMemberId = fronters[i].content.member;
userIdList.Add(fronterMemberId);
}
return userIdList.ToArray();
}
public async Task<Member[]> GetMembers(string systemId)
{
dynamic members = await get("/v1/members/" + systemId);
List<Member> memberList = new List<Member>();
for(int i = 0; i < members.Count; i++)
{
bool sysMemberExist = members[i].exists;
if (!sysMemberExist) continue;
string sysMemberId = members[i].id;
string sysMemberSystemId = members[i].content.uid;
string sysMemberName = members[i].content.name;
string sysMemberPronouns = members[i].content.pronouns;
string sysMemberAvaUrl = members[i].content.avatarUrl;
string sysMemberAvaUid = members[i].content.avatarUuid;
memberList.Add(new Member(sysMemberSystemId, sysMemberName, sysMemberPronouns, sysMemberId, sysMemberAvaUid, sysMemberAvaUrl));
}
return memberList.ToArray();
}
public Rest(string token) : base(token)
{
client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Plural Rich Presence/1.0;");
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", token);
}
}
}

View File

@ -0,0 +1,117 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence.SimplyPlural
{
public class Socket : ApiType
{
public event EventHandler FronterChanged;
ClientWebSocket wss = new ClientWebSocket();
Timer? keepAliveTimer = null;
private void onFronterChanged(dynamic fronterChangedEventData)
{
if(FronterChanged is not null)
{
FronterChanged(this, new EventArgs());
}
}
private async Task<string> receiveMessageText()
{
byte[] byteArray = await receiveMessageBytes();
return Encoding.UTF8.GetString(byteArray);
}
private async Task<byte[]> receiveMessageBytes()
{
List<byte> totalPayload = new List<byte>();
byte[] buffer = new byte[0x8000];
while (wss.State == WebSocketState.Open)
{
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();
}
return totalPayload.ToArray();
}
private async Task doUpdate(dynamic jsonData)
{
string target = jsonData.target;
switch (target)
{
case "frontHistory":
onFronterChanged(jsonData);
break;
}
}
private async Task receiveTask()
{
while (wss.State == WebSocketState.Open)
{
string message = await receiveMessageText();
if (message == "pong") continue;
Console.WriteLine("< " + message);
try
{
dynamic? jsonData = JsonConvert.DeserializeObject(message);
if (jsonData is null) continue;
string type = jsonData.msg;
if(type == "update")
{
await doUpdate(jsonData);
}
}
catch (Exception) { };
}
}
private async Task connect()
{
await wss.ConnectAsync(new Uri("wss://api.apparyllis.com/v1/socket"), CancellationToken.None);
_ = receiveTask();
keepAliveTimer = new Timer((TimerCallback) => { _ = sendKeepAlive(); }, null, 0, 10 * 1000);
}
private async Task sendText(string text)
{
if (wss.State != WebSocketState.Open)
await connect();
Console.WriteLine("> " + text);
await wss.SendAsync(Encoding.UTF8.GetBytes(text), WebSocketMessageType.Text, true, CancellationToken.None);
}
private async Task sendKeepAlive()
{
await sendText("ping");
}
private async Task sendLogin()
{
dynamic data = new ExpandoObject();
data.op = "authenticate";
data.token = token;
string authenticateJson = JsonConvert.SerializeObject(data);
await sendText(authenticateJson);
}
public Socket(string token) : base(token)
{
_ = sendLogin();
}
}
}

View File

@ -0,0 +1,71 @@
using Newtonsoft.Json.Linq;
using PluralRichPresence.SimplyPlural;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace PluralRichPresence
{
public class System
{
public event EventHandler FronterChanged;
string token;
public Member[] Members;
public string SystemId;
private Socket sock;
private Rest rest;
private ManualResetEvent waitHandle = new ManualResetEvent(false);
public Member? LookupMember(string memberId)
{
foreach(Member member in this.Members)
if (member.Id == memberId) return member;
return null;
}
public async Task<Member[]> GetCurrentFronterInfo()
{
string[] fronters = await rest.GetCurrentFronters();
List<Member> members = new List<Member>();
foreach (string fronter in fronters)
{
Member? frontingMember = LookupMember(fronter);
if (frontingMember is not null) members.Add(frontingMember);
}
return members.ToArray();
}
private async Task setup()
{
this.SystemId = await rest.GetSystemId();
this.Members = await rest.GetMembers(this.SystemId);
this.sock.FronterChanged += OnFronterChange;
waitHandle.Set();
}
private void OnFronterChange(object? sender, EventArgs e)
{
if (FronterChanged is not null)
FronterChanged(sender, e);
}
public System(string simplyPluralToken)
{
this.token = simplyPluralToken;
this.rest = new Rest(this.token);
this.sock = new Socket(this.token);
_ = setup();
waitHandle.WaitOne();
}
}
}