From 5c66fe8621154b47afd8b65f0fe5116da11d14a3 Mon Sep 17 00:00:00 2001 From: karashiiro <49822414+karashiiro@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:31:38 -0700 Subject: [PATCH] Add text event log database in debug builds --- scripts/db/QueryTextEvents.sql | 9 +++++++ .../Model/TextEventLogEntry.cs | 10 +++++++ .../Service/TextEventLogCollection.cs | 27 +++++++++++++++++++ src/TextToTalk/Events/TextEvent.cs | 17 ++++++++++-- src/TextToTalk/ObjectMapper.cs | 24 +++++++++++++++++ src/TextToTalk/TextToTalk.cs | 27 +++++++++++++++++-- 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 scripts/db/QueryTextEvents.sql create mode 100644 src/TextToTalk.Data/Model/TextEventLogEntry.cs create mode 100644 src/TextToTalk.Data/Service/TextEventLogCollection.cs create mode 100644 src/TextToTalk/ObjectMapper.cs diff --git a/scripts/db/QueryTextEvents.sql b/scripts/db/QueryTextEvents.sql new file mode 100644 index 00000000..045aff7d --- /dev/null +++ b/scripts/db/QueryTextEvents.sql @@ -0,0 +1,9 @@ +SELECT Timestamp, + LAST(SPLIT(FIRST(SPLIT(Event._type, ', ')), '.')) AS Type, + Event.Source AS Source, + Event.ChatType AS ChatType, + COALESCE(Event.Speaker, Event.SpeakerName) AS Speaker, + Event.Text AS Text, + Event +FROM event +ORDER BY Timestamp DESC; \ No newline at end of file diff --git a/src/TextToTalk.Data/Model/TextEventLogEntry.cs b/src/TextToTalk.Data/Model/TextEventLogEntry.cs new file mode 100644 index 00000000..52e2cfb6 --- /dev/null +++ b/src/TextToTalk.Data/Model/TextEventLogEntry.cs @@ -0,0 +1,10 @@ +namespace TextToTalk.Data.Model; + +public class TextEventLogEntry +{ + public Guid Id { get; init; } + + public required DateTime Timestamp { get; init; } + + public required object Event { get; init; } +} \ No newline at end of file diff --git a/src/TextToTalk.Data/Service/TextEventLogCollection.cs b/src/TextToTalk.Data/Service/TextEventLogCollection.cs new file mode 100644 index 00000000..354db6c4 --- /dev/null +++ b/src/TextToTalk.Data/Service/TextEventLogCollection.cs @@ -0,0 +1,27 @@ +using LiteDB; +using TextToTalk.Data.Model; + +namespace TextToTalk.Data.Service; + +public class TextEventLogCollection(ILiteDatabase db) +{ + private const string TextEventLogCollectionName = "event"; + + public void StoreEvent(TextEventLogEntry entry) + { + var collection = GetTextEventLogCollection(); + collection.Insert(entry); + } + + private ILiteCollection GetTextEventLogCollection() + { + var collection = db.GetCollection(TextEventLogCollectionName); + EnsureIndices(collection); + return collection; + } + + private static void EnsureIndices(ILiteCollection collection) + { + collection.EnsureIndex(p => p.Timestamp); + } +} \ No newline at end of file diff --git a/src/TextToTalk/Events/TextEvent.cs b/src/TextToTalk/Events/TextEvent.cs index 1ccd5215..2347f91e 100644 --- a/src/TextToTalk/Events/TextEvent.cs +++ b/src/TextToTalk/Events/TextEvent.cs @@ -1,3 +1,16 @@ -namespace TextToTalk.Events; +using System; +using TextToTalk.Data.Model; -public abstract class TextEvent; +namespace TextToTalk.Events; + +public abstract class TextEvent +{ + public TextEventLogEntry ToLogEntry() + { + return new TextEventLogEntry + { + Event = this, + Timestamp = DateTime.UtcNow, + }; + } +} \ No newline at end of file diff --git a/src/TextToTalk/ObjectMapper.cs b/src/TextToTalk/ObjectMapper.cs new file mode 100644 index 00000000..e33eea3c --- /dev/null +++ b/src/TextToTalk/ObjectMapper.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.CompilerServices; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.Text.SeStringHandling; +using LiteDB; + +namespace TextToTalk; + +public class ObjectMapper +{ + [ModuleInitializer] + internal static void Initialize() + { + // Register one-way SeString mapper + BsonMapper.Global.RegisterType( + serialize: value => value.TextValue, + deserialize: _ => throw new NotSupportedException()); + + // Register one-way GameObject mapper + BsonMapper.Global.RegisterType( + serialize: value => value.Name.TextValue, + deserialize: _ => throw new NotSupportedException()); + } +} \ No newline at end of file diff --git a/src/TextToTalk/TextToTalk.cs b/src/TextToTalk/TextToTalk.cs index f1e94cbb..76413cac 100644 --- a/src/TextToTalk/TextToTalk.cs +++ b/src/TextToTalk/TextToTalk.cs @@ -4,6 +4,7 @@ using Dalamud.IoC; using Dalamud.Plugin; using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; @@ -22,6 +23,7 @@ using TextToTalk.Backends.Websocket; using TextToTalk.CommandModules; using TextToTalk.Data.Service; +using TextToTalk.Events; using TextToTalk.GameEnums; using TextToTalk.Middleware; using TextToTalk.Talk; @@ -70,6 +72,9 @@ public partial class TextToTalk : IDalamudPlugin private readonly IDisposable unsubscribeAll; + private ILiteDatabase? textEventLogDatabase; + private TextEventLogCollection? textEventLog; + private bool failedToBindWsPort; private bool notifiedFailedToBindPort; private bool notifiedNoPresetsConfigured; @@ -97,6 +102,7 @@ public TextToTalk( this.framework = framework; CreateDatabasePath(); + CreateEventLogDatabase(); this.database = new LiteDatabase(GetDatabasePath("TextToTalk.db")); var playerCollection = new PlayerCollection(this.database); var npcCollection = new NpcCollection(this.database); @@ -178,30 +184,46 @@ private string GetDatabasePath(string fileName) return Path.Combine(this.pluginInterface.GetPluginConfigDirectory(), fileName); } + [Conditional("DEBUG")] + private void CreateEventLogDatabase() + { + this.textEventLogDatabase = new LiteDatabase(GetDatabasePath("log.db")); + this.textEventLog = new TextEventLogCollection(this.textEventLogDatabase); + } + private IDisposable HandleTextCancel() { return OnTextSourceCancel() .Where(this, static (_, p) => p.config is { Enabled: true, CancelSpeechOnTextAdvance: true }) + .Do(LogTextEvent) .SubscribeOnThreadPool() .Subscribe( ev => FunctionalUtils.RunSafely( () => this.backendManager.CancelSay(ev.Source), ex => DetailedLog.Error(ex, "Failed to handle text cancel event")), ex => DetailedLog.Error(ex, "Text cancel event sequence has faulted"), - _ => {}); + _ => { }); } private IDisposable HandleTextEmit() { return OnTextEmit() .Where(this, static (_, p) => p.config.Enabled) + .Do(LogTextEvent) .SubscribeOnThreadPool() .Subscribe( ev => FunctionalUtils.RunSafely( () => Say(ev.Speaker, ev.SpeakerName, ev.Text.TextValue, ev.Source), ex => DetailedLog.Error(ex, "Failed to handle text emit event")), ex => DetailedLog.Error(ex, "Text emit event sequence has faulted"), - _ => {}); + _ => { }); + } + + private void LogTextEvent(TextEvent ev) + { + FunctionalUtils.RunSafely( + () => this.textEventLog?.StoreEvent(ev.ToLogEntry()), + ex => DetailedLog.Error(ex, "Failed to log text emit event")); } private IDisposable HandleFailedToBindWSPort() @@ -468,6 +490,7 @@ protected virtual void Dispose(bool disposing) this.backendManager.Dispose(); this.http.Dispose(); + this.textEventLogDatabase?.Dispose(); this.database.Dispose(); this.addonBattleTalkManager.Dispose();