Make Discord Presence logging more verbose

This commit is contained in:
pizzaboxer 2023-02-18 11:20:27 +00:00
parent f738ffc349
commit 52313cf2af

View File

@ -15,80 +15,92 @@ namespace Bloxstrap.Helpers.Integrations
{ {
class DiscordRichPresence : IDisposable class DiscordRichPresence : IDisposable
{ {
readonly DiscordRpcClient RichPresence = new("1005469189907173486"); private readonly DiscordRpcClient _rpcClient = new("1005469189907173486");
const string GameJoiningEntry = "[FLog::Output] ! Joining game"; // i'm thinking the functionality for parsing roblox logs could be broadened for more features than just rich presence,
const string GameJoinedEntry = "[FLog::Network] serverId:"; // like checking the ping and region of the current connected server. maybe that's something to add?
const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:"; private const string GameJoiningEntry = "[FLog::Output] ! Joining game";
private const string GameJoinedEntry = "[FLog::Network] serverId:";
private const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:";
const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)"; private const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)";
const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|([0-9]+)"; private const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|([0-9]+)";
private int _logEntriesRead = 0;
// these are values to use assuming the player isn't currently in a game // these are values to use assuming the player isn't currently in a game
bool ActivityInGame = false; private bool _activityInGame = false;
long ActivityPlaceId = 0; private long _activityPlaceId = 0;
string ActivityJobId = ""; private string _activityJobId = "";
string ActivityMachineAddress = ""; // we're only really using this to confirm a place join. todo: maybe this could be used to see server location/ping? private string _activityMachineAddress = "";
public DiscordRichPresence() public DiscordRichPresence()
{ {
RichPresence.OnReady += (_, e) => _rpcClient.OnReady += (_, e) =>
App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Received ready from user {e.User.Username} ({e.User.ID})"); App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Received ready from user {e.User.Username} ({e.User.ID})");
RichPresence.OnPresenceUpdate += (_, e) => _rpcClient.OnPresenceUpdate += (_, e) =>
App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Updated presence"); App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Updated presence");
RichPresence.OnConnectionEstablished += (_, e) => _rpcClient.OnConnectionEstablished += (_, e) =>
App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Established connection with Discord RPC"); App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Established connection with Discord RPC");
//spams log as it tries to connect every ~15 sec when discord is closed so not now //spams log as it tries to connect every ~15 sec when discord is closed so not now
//RichPresence.OnConnectionFailed += (_, e) => //_rpcClient.OnConnectionFailed += (_, e) =>
// App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Failed to establish connection with Discord RPC"); // App.Logger.WriteLine("[DiscordRichPresence::DiscordRichPresence] Failed to establish connection with Discord RPC");
RichPresence.OnClose += (_, e) => _rpcClient.OnClose += (_, e) =>
App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Lost connection to Discord RPC - {e.Reason} ({e.Code})"); App.Logger.WriteLine($"[DiscordRichPresence::DiscordRichPresence] Lost connection to Discord RPC - {e.Reason} ({e.Code})");
RichPresence.Initialize(); _rpcClient.Initialize();
} }
private async Task ExamineLogEntry(string entry) private async Task ExamineLogEntry(string entry)
{ {
// App.Logger.WriteLine(entry); // App.Logger.WriteLine(entry);
_logEntriesRead += 1;
if (entry.Contains(GameJoiningEntry) && !ActivityInGame && ActivityPlaceId == 0) // debug stats to ensure that the log reader is working correctly
// if more than 5000 log entries have been read, only log per 100 to save on spam
if (_logEntriesRead <= 5000 && _logEntriesRead % 50 == 0)
App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Read {_logEntriesRead} log entries");
else if (_logEntriesRead % 100 == 0)
App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Read {_logEntriesRead} log entries");
if (entry.Contains(GameJoiningEntry) && !_activityInGame && _activityPlaceId == 0)
{ {
Match match = Regex.Match(entry, GameJoiningEntryPattern); Match match = Regex.Match(entry, GameJoiningEntryPattern);
if (match.Groups.Count != 4) if (match.Groups.Count != 4)
return; return;
ActivityInGame = false; _activityInGame = false;
ActivityPlaceId = Int64.Parse(match.Groups[2].Value); _activityPlaceId = Int64.Parse(match.Groups[2].Value);
ActivityJobId = match.Groups[1].Value; _activityJobId = match.Groups[1].Value;
ActivityMachineAddress = match.Groups[3].Value; _activityMachineAddress = match.Groups[3].Value;
App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Joining Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})"); App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Joining Game ({_activityPlaceId}/{_activityJobId}/{_activityMachineAddress})");
} }
else if (entry.Contains(GameJoinedEntry) && !ActivityInGame && ActivityPlaceId != 0) else if (entry.Contains(GameJoinedEntry) && !_activityInGame && _activityPlaceId != 0)
{ {
Match match = Regex.Match(entry, GameJoinedEntryPattern); Match match = Regex.Match(entry, GameJoinedEntryPattern);
if (match.Groups.Count != 3 || match.Groups[1].Value != ActivityMachineAddress) if (match.Groups.Count != 3 || match.Groups[1].Value != _activityMachineAddress)
return; return;
App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Joined Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})"); App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Joined Game ({_activityPlaceId}/{_activityJobId}/{_activityMachineAddress})");
ActivityInGame = true; _activityInGame = true;
await SetPresence(); await SetPresence();
} }
else if (entry.Contains(GameDisconnectedEntry) && ActivityInGame && ActivityPlaceId != 0) else if (entry.Contains(GameDisconnectedEntry) && _activityInGame && _activityPlaceId != 0)
{ {
App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Disconnected from Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})"); App.Logger.WriteLine($"[DiscordRichPresence::ExamineLogEntry] Disconnected from Game ({_activityPlaceId}/{_activityJobId}/{_activityMachineAddress})");
ActivityInGame = false; _activityInGame = false;
ActivityPlaceId = 0; _activityPlaceId = 0;
ActivityJobId = ""; _activityJobId = "";
ActivityMachineAddress = ""; _activityMachineAddress = "";
await SetPresence(); await SetPresence();
} }
} }
@ -117,6 +129,8 @@ namespace Bloxstrap.Helpers.Integrations
// if roblox doesn't start quickly enough, we can wind up fetching the previous log file // if roblox doesn't start quickly enough, we can wind up fetching the previous log file
// good rule of thumb is to find a log file that was created in the last 15 seconds or so // good rule of thumb is to find a log file that was created in the last 15 seconds or so
App.Logger.WriteLine("[DiscordRichPresence::MonitorGameActivity] Opening Roblox log file...");
while (true) while (true)
{ {
logFileInfo = new DirectoryInfo(logDirectory).GetFiles().OrderByDescending(x => x.CreationTime).First(); logFileInfo = new DirectoryInfo(logDirectory).GetFiles().OrderByDescending(x => x.CreationTime).First();
@ -124,10 +138,12 @@ namespace Bloxstrap.Helpers.Integrations
if (logFileInfo.CreationTime.AddSeconds(15) > DateTime.Now) if (logFileInfo.CreationTime.AddSeconds(15) > DateTime.Now)
break; break;
App.Logger.WriteLine($"[DiscordRichPresence::MonitorGameActivity] Could not find recent enough log file, waiting... (newest is {logFileInfo.Name})");
await Task.Delay(1000); await Task.Delay(1000);
} }
FileStream logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); FileStream logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
App.Logger.WriteLine($"[DiscordRichPresence::MonitorGameActivity] Opened {logFileInfo.Name}");
AutoResetEvent logUpdatedEvent = new(false); AutoResetEvent logUpdatedEvent = new(false);
FileSystemWatcher logWatcher = new() FileSystemWatcher logWatcher = new()
@ -138,74 +154,66 @@ namespace Bloxstrap.Helpers.Integrations
}; };
logWatcher.Changed += (s, e) => logUpdatedEvent.Set(); logWatcher.Changed += (s, e) => logUpdatedEvent.Set();
using (StreamReader sr = new(logFileStream)) using StreamReader sr = new(logFileStream);
{
string? log = null;
while (true) while (true)
{ {
log = await sr.ReadLineAsync(); string? log = await sr.ReadLineAsync();
if (String.IsNullOrEmpty(log)) if (String.IsNullOrEmpty(log))
{
logUpdatedEvent.WaitOne(1000); logUpdatedEvent.WaitOne(1000);
}
else else
{
//App.Logger.WriteLine(log);
await ExamineLogEntry(log); await ExamineLogEntry(log);
} }
}
}
// no need to close the event, its going to be finished with when the program closes anyway // no need to close the event, its going to be finished with when the program closes anyway
} }
public async Task<bool> SetPresence() public async Task<bool> SetPresence()
{ {
if (!ActivityInGame) if (!_activityInGame)
{ {
RichPresence.ClearPresence(); _rpcClient.ClearPresence();
return true; return true;
} }
string placeThumbnail = "roblox"; string placeThumbnail = "roblox";
var placeInfo = await Utilities.GetJson<RobloxAsset>($"https://economy.roblox.com/v2/assets/{ActivityPlaceId}/details"); var placeInfo = await Utilities.GetJson<RobloxAsset>($"https://economy.roblox.com/v2/assets/{_activityPlaceId}/details");
if (placeInfo is null || placeInfo.Creator is null) if (placeInfo is null || placeInfo.Creator is null)
return false; return false;
var thumbnailInfo = await Utilities.GetJson<RobloxThumbnails>($"https://thumbnails.roblox.com/v1/places/gameicons?placeIds={ActivityPlaceId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false"); var thumbnailInfo = await Utilities.GetJson<RobloxThumbnails>($"https://thumbnails.roblox.com/v1/places/gameicons?placeIds={_activityPlaceId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false");
if (thumbnailInfo is not null) if (thumbnailInfo is not null)
placeThumbnail = thumbnailInfo.Data![0].ImageUrl!; placeThumbnail = thumbnailInfo.Data![0].ImageUrl!;
List<DiscordRPC.Button> buttons = new() List<Button> buttons = new()
{ {
new DiscordRPC.Button() new Button
{ {
Label = "See Details", Label = "See Details",
Url = $"https://www.roblox.com/games/{ActivityPlaceId}" Url = $"https://www.roblox.com/games/{_activityPlaceId}"
} }
}; };
if (!App.Settings.Prop.HideRPCButtons) if (!App.Settings.Prop.HideRPCButtons)
{ {
buttons.Insert(0, new DiscordRPC.Button() buttons.Insert(0, new Button
{ {
Label = "Join", Label = "Join",
Url = $"https://www.roblox.com/games/start?placeId={ActivityPlaceId}&gameInstanceId={ActivityJobId}&launchData=%7B%7D" Url = $"https://www.roblox.com/games/start?placeId={_activityPlaceId}&gameInstanceId={_activityJobId}&launchData=%7B%7D"
}); });
} }
RichPresence.SetPresence(new RichPresence() _rpcClient.SetPresence(new RichPresence
{ {
Details = placeInfo.Name, Details = placeInfo.Name,
State = $"by {placeInfo.Creator.Name}", State = $"by {placeInfo.Creator.Name}",
Timestamps = new Timestamps() { Start = DateTime.UtcNow }, Timestamps = new Timestamps { Start = DateTime.UtcNow },
Buttons = buttons.ToArray(), Buttons = buttons.ToArray(),
Assets = new Assets() Assets = new Assets
{ {
LargeImageKey = placeThumbnail, LargeImageKey = placeThumbnail,
LargeImageText = placeInfo.Name, LargeImageText = placeInfo.Name,
@ -220,8 +228,8 @@ namespace Bloxstrap.Helpers.Integrations
public void Dispose() public void Dispose()
{ {
App.Logger.WriteLine("[DiscordRichPresence::Dispose] Cleaning up Discord RPC and Presence"); App.Logger.WriteLine("[DiscordRichPresence::Dispose] Cleaning up Discord RPC and Presence");
RichPresence.ClearPresence(); _rpcClient.ClearPresence();
RichPresence.Dispose(); _rpcClient.Dispose();
} }
} }
} }