diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..add57be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..1b22344
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "LibMatrix"]
+ path = LibMatrix
+ url = rory.gay:git/matrix/LibMatrix.git
diff --git a/.idea/.idea.MatrixLogFwd/.idea/.gitignore b/.idea/.idea.MatrixLogFwd/.idea/.gitignore
new file mode 100644
index 0000000..d94223f
--- /dev/null
+++ b/.idea/.idea.MatrixLogFwd/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/modules.xml
+/projectSettingsUpdater.xml
+/.idea.MatrixLogFwd.iml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.MatrixLogFwd/.idea/encodings.xml b/.idea/.idea.MatrixLogFwd/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.MatrixLogFwd/.idea/encodings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
+</project>
\ No newline at end of file
diff --git a/.idea/.idea.MatrixLogFwd/.idea/indexLayout.xml b/.idea/.idea.MatrixLogFwd/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.MatrixLogFwd/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="UserContentModel">
+ <attachedFolders />
+ <explicitIncludes />
+ <explicitExcludes />
+ </component>
+</project>
\ No newline at end of file
diff --git a/.idea/.idea.MatrixLogFwd/.idea/vcs.xml b/.idea/.idea.MatrixLogFwd/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.MatrixLogFwd/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
diff --git a/LibMatrix b/LibMatrix
new file mode 160000
+Subproject bf1664b254bfc224f0087eb82fdba5235fbd162
diff --git a/MatrixLogFwd.sln b/MatrixLogFwd.sln
new file mode 100644
index 0000000..a5d8daa
--- /dev/null
+++ b/MatrixLogFwd.sln
@@ -0,0 +1,156 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+#
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatrixLogFwd", "MatrixLogFwd\MatrixLogFwd.csproj", "{273CD337-1EAF-407A-8F74-F019701413FA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LibMatrix", "LibMatrix", "{46F8B9D3-1581-456A-A994-66860D17D009}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.EventTypes", "LibMatrix\LibMatrix.EventTypes\LibMatrix.EventTypes.csproj", "{264233FA-8C39-4280-A636-183F28B597CC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix", "LibMatrix\LibMatrix\LibMatrix.csproj", "{18E952CA-6C34-42B8-9655-704E4FC23085}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{91DB6B55-A456-46AC-8EE6-0C4C3D628D5E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Tests", "LibMatrix\Tests\LibMatrix.Tests\LibMatrix.Tests.csproj", "{B59DCECF-FD2B-4869-ACB6-749F5DA58A9B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{510C170A-58A2-4348-BC8E-FEA68D709C74}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.DebugDataValidationApi", "LibMatrix\Utilities\LibMatrix.DebugDataValidationApi\LibMatrix.DebugDataValidationApi.csproj", "{FF9C7A5D-168F-43D1-929A-0053A1E5EA9A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.DevTestBot", "LibMatrix\Utilities\LibMatrix.DevTestBot\LibMatrix.DevTestBot.csproj", "{6184F4FC-55BA-48BF-A8E9-CFF091B65A06}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.E2eeTestKit", "LibMatrix\Utilities\LibMatrix.E2eeTestKit\LibMatrix.E2eeTestKit.csproj", "{037EA432-A9EE-44CA-A4FC-D7E8433705F3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.HomeserverEmulator", "LibMatrix\Utilities\LibMatrix.HomeserverEmulator\LibMatrix.HomeserverEmulator.csproj", "{9FCCD302-0734-48DE-BF51-D4982396659E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.JsonSerializerContextGenerator", "LibMatrix\Utilities\LibMatrix.JsonSerializerContextGenerator\LibMatrix.JsonSerializerContextGenerator.csproj", "{41A1ACF4-BCD2-4080-9799-BEBEAE93D27A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.TestDataGenerator", "LibMatrix\Utilities\LibMatrix.TestDataGenerator\LibMatrix.TestDataGenerator.csproj", "{45241F77-49FF-4EAF-9056-063CF524CA32}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Utilities.Bot", "LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj", "{1E540FF4-BC79-4C8D-AA55-E2ED975B899C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ArcaneLibs", "ArcaneLibs", "{C77C2186-30F5-4DA8-9865-B2CD0B62043E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs", "LibMatrix\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj", "{3B4049DF-25B1-48A9-BE2C-14721489D4E2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.Blazor.Components", "LibMatrix\ArcaneLibs\ArcaneLibs.Blazor.Components\ArcaneLibs.Blazor.Components.csproj", "{31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.Legacy", "LibMatrix\ArcaneLibs\ArcaneLibs.Legacy\ArcaneLibs.Legacy.csproj", "{E02067D7-09CD-4567-99A4-1FE6410E4483}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.Logging", "LibMatrix\ArcaneLibs\ArcaneLibs.Logging\ArcaneLibs.Logging.csproj", "{89EEC6CA-8653-4137-8E35-C46F863F1406}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.StringNormalisation", "LibMatrix\ArcaneLibs\ArcaneLibs.StringNormalisation\ArcaneLibs.StringNormalisation.csproj", "{EC0680D8-35C4-49E6-8523-2F04479FFC9A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.Timings", "LibMatrix\ArcaneLibs\ArcaneLibs.Timings\ArcaneLibs.Timings.csproj", "{C90DB861-D3AD-4E73-BE12-219B7A41047C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLibs.UsageTest", "LibMatrix\ArcaneLibs\ArcaneLibs.UsageTest\ArcaneLibs.UsageTest.csproj", "{F2FE34EE-5718-47AC-AAF1-44B0F3B367AC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcaneLib.Tests", "LibMatrix\ArcaneLibs\ArcaneLib.Tests\ArcaneLib.Tests.csproj", "{C0A1A9F9-7B65-488E-8880-F937102FDAED}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {273CD337-1EAF-407A-8F74-F019701413FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {273CD337-1EAF-407A-8F74-F019701413FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {273CD337-1EAF-407A-8F74-F019701413FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {273CD337-1EAF-407A-8F74-F019701413FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {264233FA-8C39-4280-A636-183F28B597CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {264233FA-8C39-4280-A636-183F28B597CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {264233FA-8C39-4280-A636-183F28B597CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {264233FA-8C39-4280-A636-183F28B597CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {18E952CA-6C34-42B8-9655-704E4FC23085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {18E952CA-6C34-42B8-9655-704E4FC23085}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {18E952CA-6C34-42B8-9655-704E4FC23085}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {18E952CA-6C34-42B8-9655-704E4FC23085}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B59DCECF-FD2B-4869-ACB6-749F5DA58A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B59DCECF-FD2B-4869-ACB6-749F5DA58A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B59DCECF-FD2B-4869-ACB6-749F5DA58A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B59DCECF-FD2B-4869-ACB6-749F5DA58A9B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FF9C7A5D-168F-43D1-929A-0053A1E5EA9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FF9C7A5D-168F-43D1-929A-0053A1E5EA9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FF9C7A5D-168F-43D1-929A-0053A1E5EA9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FF9C7A5D-168F-43D1-929A-0053A1E5EA9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6184F4FC-55BA-48BF-A8E9-CFF091B65A06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6184F4FC-55BA-48BF-A8E9-CFF091B65A06}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6184F4FC-55BA-48BF-A8E9-CFF091B65A06}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6184F4FC-55BA-48BF-A8E9-CFF091B65A06}.Release|Any CPU.Build.0 = Release|Any CPU
+ {037EA432-A9EE-44CA-A4FC-D7E8433705F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {037EA432-A9EE-44CA-A4FC-D7E8433705F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {037EA432-A9EE-44CA-A4FC-D7E8433705F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {037EA432-A9EE-44CA-A4FC-D7E8433705F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9FCCD302-0734-48DE-BF51-D4982396659E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9FCCD302-0734-48DE-BF51-D4982396659E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9FCCD302-0734-48DE-BF51-D4982396659E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9FCCD302-0734-48DE-BF51-D4982396659E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41A1ACF4-BCD2-4080-9799-BEBEAE93D27A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41A1ACF4-BCD2-4080-9799-BEBEAE93D27A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41A1ACF4-BCD2-4080-9799-BEBEAE93D27A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41A1ACF4-BCD2-4080-9799-BEBEAE93D27A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45241F77-49FF-4EAF-9056-063CF524CA32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45241F77-49FF-4EAF-9056-063CF524CA32}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45241F77-49FF-4EAF-9056-063CF524CA32}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45241F77-49FF-4EAF-9056-063CF524CA32}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E540FF4-BC79-4C8D-AA55-E2ED975B899C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1E540FF4-BC79-4C8D-AA55-E2ED975B899C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E540FF4-BC79-4C8D-AA55-E2ED975B899C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1E540FF4-BC79-4C8D-AA55-E2ED975B899C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3B4049DF-25B1-48A9-BE2C-14721489D4E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B4049DF-25B1-48A9-BE2C-14721489D4E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B4049DF-25B1-48A9-BE2C-14721489D4E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B4049DF-25B1-48A9-BE2C-14721489D4E2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E02067D7-09CD-4567-99A4-1FE6410E4483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E02067D7-09CD-4567-99A4-1FE6410E4483}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E02067D7-09CD-4567-99A4-1FE6410E4483}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E02067D7-09CD-4567-99A4-1FE6410E4483}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89EEC6CA-8653-4137-8E35-C46F863F1406}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89EEC6CA-8653-4137-8E35-C46F863F1406}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89EEC6CA-8653-4137-8E35-C46F863F1406}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89EEC6CA-8653-4137-8E35-C46F863F1406}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EC0680D8-35C4-49E6-8523-2F04479FFC9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EC0680D8-35C4-49E6-8523-2F04479FFC9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EC0680D8-35C4-49E6-8523-2F04479FFC9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EC0680D8-35C4-49E6-8523-2F04479FFC9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C90DB861-D3AD-4E73-BE12-219B7A41047C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C90DB861-D3AD-4E73-BE12-219B7A41047C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C90DB861-D3AD-4E73-BE12-219B7A41047C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C90DB861-D3AD-4E73-BE12-219B7A41047C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2FE34EE-5718-47AC-AAF1-44B0F3B367AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2FE34EE-5718-47AC-AAF1-44B0F3B367AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2FE34EE-5718-47AC-AAF1-44B0F3B367AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2FE34EE-5718-47AC-AAF1-44B0F3B367AC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C0A1A9F9-7B65-488E-8880-F937102FDAED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C0A1A9F9-7B65-488E-8880-F937102FDAED}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C0A1A9F9-7B65-488E-8880-F937102FDAED}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C0A1A9F9-7B65-488E-8880-F937102FDAED}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {264233FA-8C39-4280-A636-183F28B597CC} = {46F8B9D3-1581-456A-A994-66860D17D009}
+ {18E952CA-6C34-42B8-9655-704E4FC23085} = {46F8B9D3-1581-456A-A994-66860D17D009}
+ {91DB6B55-A456-46AC-8EE6-0C4C3D628D5E} = {46F8B9D3-1581-456A-A994-66860D17D009}
+ {B59DCECF-FD2B-4869-ACB6-749F5DA58A9B} = {91DB6B55-A456-46AC-8EE6-0C4C3D628D5E}
+ {510C170A-58A2-4348-BC8E-FEA68D709C74} = {46F8B9D3-1581-456A-A994-66860D17D009}
+ {FF9C7A5D-168F-43D1-929A-0053A1E5EA9A} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {6184F4FC-55BA-48BF-A8E9-CFF091B65A06} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {037EA432-A9EE-44CA-A4FC-D7E8433705F3} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {9FCCD302-0734-48DE-BF51-D4982396659E} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {41A1ACF4-BCD2-4080-9799-BEBEAE93D27A} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {45241F77-49FF-4EAF-9056-063CF524CA32} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {1E540FF4-BC79-4C8D-AA55-E2ED975B899C} = {510C170A-58A2-4348-BC8E-FEA68D709C74}
+ {C77C2186-30F5-4DA8-9865-B2CD0B62043E} = {46F8B9D3-1581-456A-A994-66860D17D009}
+ {3B4049DF-25B1-48A9-BE2C-14721489D4E2} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {31F14D0B-2BAE-4E4A-BE92-C1AD70FB7EDA} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {E02067D7-09CD-4567-99A4-1FE6410E4483} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {89EEC6CA-8653-4137-8E35-C46F863F1406} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {EC0680D8-35C4-49E6-8523-2F04479FFC9A} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {C90DB861-D3AD-4E73-BE12-219B7A41047C} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {F2FE34EE-5718-47AC-AAF1-44B0F3B367AC} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ {C0A1A9F9-7B65-488E-8880-F937102FDAED} = {C77C2186-30F5-4DA8-9865-B2CD0B62043E}
+ EndGlobalSection
+EndGlobal
diff --git a/MatrixLogFwd.sln.DotSettings.user b/MatrixLogFwd.sln.DotSettings.user
new file mode 100644
index 0000000..8a59ed0
--- /dev/null
+++ b/MatrixLogFwd.sln.DotSettings.user
@@ -0,0 +1,2 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002EUnix_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F9cf5f68d759deefc91b9c48c5ac3dd27708bb7dc38d0c485661fff5ce15b82_003FSafeFileHandle_002EUnix_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/MatrixLogFwd/Config.cs b/MatrixLogFwd/Config.cs
new file mode 100644
index 0000000..547292e
--- /dev/null
+++ b/MatrixLogFwd/Config.cs
@@ -0,0 +1,7 @@
+namespace MatrixLogFwd;
+
+public class Config
+{
+ public string HomeserverBaseUrl { get; set; } = "https://matrix.org";
+ public string AccessToken { get; set; } = "";
+}
\ No newline at end of file
diff --git a/MatrixLogFwd/MatrixLogFwd.csproj b/MatrixLogFwd/MatrixLogFwd.csproj
new file mode 100644
index 0000000..387c966
--- /dev/null
+++ b/MatrixLogFwd/MatrixLogFwd.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net8.0</TargetFramework>
+ <LangVersion>preview</LangVersion>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/MatrixLogFwd/Program.cs b/MatrixLogFwd/Program.cs
new file mode 100644
index 0000000..4f2c8e1
--- /dev/null
+++ b/MatrixLogFwd/Program.cs
@@ -0,0 +1,144 @@
+// See https://aka.ms/new-console-template for more information
+
+using System.Text;
+using System.Text.Json;
+using ArcaneLibs;
+using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
+using LibMatrix.RoomTypes;
+using MatrixLogFwd;
+using Microsoft.VisualBasic.CompilerServices;
+
+Console.WriteLine("Hello, World!");
+var cfgPath = Util.ExpandPath($"~/.config/MatrixLogFwd/config.json");
+var cfg = JsonSerializer.Deserialize<Config>(System.IO.File.ReadAllText(cfgPath));
+
+var hs = await new AuthenticatedHomeserverGeneric(cfg.HomeserverBaseUrl, new(){Client = cfg.HomeserverBaseUrl}, null, cfg.AccessToken).Initialise();
+Console.WriteLine(hs);
+
+string mode = "lines";
+int chunkSize = 1000;
+string? command = null;
+GenericRoom? room = null;
+
+var argsEnum = args.AsEnumerable().GetEnumerator();
+while (argsEnum.MoveNext())
+{
+ var arg = argsEnum.Current;
+ switch (arg)
+ {
+ case "--help":
+ Console.WriteLine("Usage: MatrixLogFwd [--help] [--mode <file|lines>] [--chunk-size <int>] [-- <command>]");
+ break;
+ case "--mode":
+ argsEnum.MoveNext();
+ mode = argsEnum.Current;
+ if (mode != "file" && mode != "lines")
+ {
+ Console.WriteLine("Invalid mode");
+ return;
+ }
+ break;
+ case "--chunk-size":
+ argsEnum.MoveNext();
+ chunkSize = Conversions.ToInteger(argsEnum.Current);
+ break;
+ case "--roomid":
+ argsEnum.MoveNext();
+ var roomId = argsEnum.Current;
+ room = hs.GetRoom(roomId);
+ break;
+
+ case "--":
+ command = "";
+ while (argsEnum.MoveNext())
+ {
+ command += argsEnum.Current + " ";
+ }
+ break;
+ }
+}
+
+if (room == null)
+{
+ Console.WriteLine("No room specified, creating new room");
+ room = await hs.CreateRoom(new()
+ {
+ Name = $"MatrixLogFwd logs from {Environment.MachineName} at {DateTime.Now}",
+ });
+}
+
+var stream = Console.OpenStandardInput();
+if (!string.IsNullOrWhiteSpace(command))
+{
+ var proc = new System.Diagnostics.Process
+ {
+ StartInfo = new()
+ {
+ FileName = "/bin/sh",
+ Arguments = $"-c \"{command}\"",
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ }
+ };
+ proc.Start();
+ stream = proc.StandardOutput.BaseStream;
+}
+
+if (mode == "lines")
+{
+ var buffer = new byte[1024];
+ var line = new StringBuilder();
+ int linesInBuffer = 0;
+ while (true)
+ {
+ var read = await stream.ReadAsync(buffer);
+ Console.WriteLine($"Read {read} bytes");
+ if (read == 0)
+ {
+ await room.SendMessageEventAsync(new MessageBuilder().WithCodeBlock(line.ToString()).Build());
+ break;
+ }
+ for (int i = 0; i < read; i++)
+ {
+ if (buffer[i] == '\n')
+ {
+ linesInBuffer++;
+ if (linesInBuffer > chunkSize)
+ {
+ await room.SendMessageEventAsync(new MessageBuilder().WithCodeBlock(line.ToString()).Build());
+ line.Clear();
+ linesInBuffer = 0;
+ }
+ else
+ {
+ line.Append((char)buffer[i]);
+ }
+ }
+ else
+ {
+ line.Append((char)buffer[i]);
+ }
+ }
+ }
+}
+else
+{
+ var buffer = new byte[chunkSize];
+ while (true)
+ {
+ var read = await stream.ReadAtLeastAsync(buffer, chunkSize, false);
+ if (read == 0)
+ {
+ break;
+ }
+ Console.WriteLine($"Read {read} bytes");
+ var ms = new System.IO.MemoryStream(buffer, 0, read);
+ var filename = $"log-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt";
+ await room.SendFileAsync(filename, ms);
+ }
+}
+
+// await room.LeaveAsync();
\ No newline at end of file
|