about summary refs log tree commit diff
diff options
context:
space:
mode:
m---------ArcaneLibs0
-rw-r--r--LibMatrix.EventTypes/EventContent.cs4
-rw-r--r--LibMatrix.EventTypes/Spec/RoomMessageEventContent.cs1
-rw-r--r--LibMatrix/Extensions/MatrixHttpClient.Single.cs16
-rw-r--r--LibMatrix/Helpers/MessageBuilder.cs12
-rw-r--r--LibMatrix/Helpers/SyncHelper.cs8
-rw-r--r--LibMatrix/LibMatrix.csproj2
-rw-r--r--LibMatrix/Responses/SyncResponse.cs2
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs23
-rw-r--r--LibMatrix/Services/ServiceInstaller.cs10
-rw-r--r--LibMatrix/StateEvent.cs2
-rw-r--r--Utilities/LibMatrix.TestDataGenerator/Program.cs1
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs (renamed from Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs)26
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs17
-rw-r--r--Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs3
15 files changed, 102 insertions, 25 deletions
diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject b7685c786b29e7f8ae2db6ff0f79a52efc57020
+Subproject 952f5ef673862fdb7ff6f51d032eca4577dab9c
diff --git a/LibMatrix.EventTypes/EventContent.cs b/LibMatrix.EventTypes/EventContent.cs
index c582cf2..d65a47f 100644
--- a/LibMatrix.EventTypes/EventContent.cs
+++ b/LibMatrix.EventTypes/EventContent.cs
@@ -37,6 +37,10 @@ public abstract class TimelineEventContent : EventContent {
         [JsonPropertyName("rel_type")]
         public string? RelationType { get; set; }
 
+        // used for reactions
+        [JsonPropertyName("key")]
+        public string? Key { get; set; }
+
         public class EventInReplyTo {
             [JsonPropertyName("event_id")]
             public string? EventId { get; set; }
diff --git a/LibMatrix.EventTypes/Spec/RoomMessageEventContent.cs b/LibMatrix.EventTypes/Spec/RoomMessageEventContent.cs
index ae893f8..9602bf3 100644
--- a/LibMatrix.EventTypes/Spec/RoomMessageEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/RoomMessageEventContent.cs
@@ -29,6 +29,7 @@ public class RoomMessageEventContent : TimelineEventContent {
     [JsonPropertyName("url")]
     public string? Url { get; set; }
 
+    [JsonPropertyName("filename")]
     public string? FileName { get; set; }
 
     [JsonPropertyName("info")]
diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
index 39eb7e5..4145a16 100644
--- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs
+++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -2,6 +2,7 @@
 // #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Net;
 using System.Net.Http.Headers;
 using System.Reflection;
 using System.Security.Cryptography.X509Certificates;
@@ -73,12 +74,15 @@ public class MatrixHttpClient {
         await _rateLimitSemaphore.WaitAsync(cancellationToken);
 #endif
 
-        Console.WriteLine($"Sending {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)})");
+        Console.WriteLine($"Sending {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.GetContentLength())})");
 
         if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null");
         if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri);
         foreach (var (key, value) in AdditionalQueryParameters) request.RequestUri = request.RequestUri.AddQuery(key, value);
-        foreach (var (key, value) in DefaultRequestHeaders) request.Headers.Add(key, value);
+        foreach (var (key, value) in DefaultRequestHeaders) {
+            if (request.Headers.Contains(key)) continue;
+            request.Headers.Add(key, value);
+        }
 
         request.Options.Set(new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse"), true);
 
@@ -106,7 +110,13 @@ public class MatrixHttpClient {
     public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) {
         var responseMessage = await SendUnhandledAsync(request, cancellationToken);
         if (responseMessage.IsSuccessStatusCode) return responseMessage;
-
+        
+        //retry on gateway timeout
+        if (responseMessage.StatusCode == HttpStatusCode.GatewayTimeout) {
+            request.ResetSendStatus();
+            return await SendAsync(request, cancellationToken);
+        }
+        
         //error handling
         var content = await responseMessage.Content.ReadAsStringAsync(cancellationToken);
         if (content.Length == 0)
diff --git a/LibMatrix/Helpers/MessageBuilder.cs b/LibMatrix/Helpers/MessageBuilder.cs
index d897078..b639e1f 100644
--- a/LibMatrix/Helpers/MessageBuilder.cs
+++ b/LibMatrix/Helpers/MessageBuilder.cs
@@ -91,6 +91,18 @@ public class MessageBuilder(string msgType = "m.text", string format = "org.matr
         return this;
     }
 
+    public MessageBuilder WithMention(string id, string? displayName = null) {
+        Content.Body += $"@{displayName ?? id}";
+        Content.FormattedBody += $"<a href=\"https://matrix.to/#/{id}\">{displayName ?? id}</a>";
+        return this;
+    }
+
+    public MessageBuilder WithNewline() {
+        Content.Body += "\n";
+        Content.FormattedBody += "<br>";
+        return this;
+    }
+
     public MessageBuilder WithTable(Action<TableBuilder> tableBuilder) {
         var tb = new TableBuilder(this);
         this.WithHtmlTag("table", msb => tableBuilder(tb));
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index 1833bd0..c9ca85d 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -4,6 +4,7 @@ using ArcaneLibs.Extensions;
 using LibMatrix.Filters;
 using LibMatrix.Homeservers;
 using LibMatrix.Responses;
+using LibMatrix.Utilities;
 using Microsoft.Extensions.Logging;
 
 namespace LibMatrix.Helpers;
@@ -42,6 +43,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
             _filter = value;
             _filterIsDirty = true;
             _filterId = null;
+            _namedFilterName = null;
         }
     }
 
@@ -81,16 +83,16 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
         if (!string.IsNullOrWhiteSpace(Since)) url += $"&since={Since}";
         if (_filterId is not null) url += $"&filter={_filterId}";
 
-        logger?.LogInformation("SyncHelper: Calling: {}", url);
+        // logger?.LogInformation("SyncHelper: Calling: {}", url);
 
         try {
             var httpResp = await homeserver.ClientHttpClient.GetAsync(url, cancellationToken ?? CancellationToken.None);
             if (httpResp is null) throw new NullReferenceException("Failed to send HTTP request");
-            logger?.LogInformation("Got sync response: {} bytes, {} elapsed", httpResp.Content.Headers.ContentLength ?? -1, sw.Elapsed);
+            logger?.LogTrace("Got sync response: {} bytes, {} elapsed", httpResp.GetContentLength(), sw.Elapsed);
             var deserializeSw = Stopwatch.StartNew();
             var resp = await httpResp.Content.ReadFromJsonAsync<SyncResponse>(cancellationToken: cancellationToken ?? CancellationToken.None,
                 jsonTypeInfo: SyncResponseSerializerContext.Default.SyncResponse);
-            logger?.LogInformation("Deserialized sync response: {} bytes, {} elapsed, {} total", httpResp.Content.Headers.ContentLength ?? -1, deserializeSw.Elapsed, sw.Elapsed);
+            logger?.LogInformation("Deserialized sync response: {} bytes, {} elapsed, {} total", httpResp.GetContentLength(), deserializeSw.Elapsed, sw.Elapsed);
             var timeToWait = MinimumDelay.Subtract(sw.Elapsed);
             if (timeToWait.TotalMilliseconds > 0)
                 await Task.Delay(timeToWait);
diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj
index 1e3a8aa..6158ff8 100644
--- a/LibMatrix/LibMatrix.csproj
+++ b/LibMatrix/LibMatrix.csproj
@@ -27,8 +27,10 @@
         <ProjectReference Include="..\LibMatrix.EventTypes\LibMatrix.EventTypes.csproj"/>
     </ItemGroup>
 
+    <!-- 
     <Target Name="ArcaneLibsNugetWarning" AfterTargets="AfterBuild">
         <Warning Text="ArcaneLibs is being referenced from NuGet, which is dangerous. Please read the warning in LibMatrix.csproj!" Condition="!Exists('..\ArcaneLibs\ArcaneLibs\ArcaneLibs.csproj')"/>
     </Target>
+    -->
 
 </Project>
diff --git a/LibMatrix/Responses/SyncResponse.cs b/LibMatrix/Responses/SyncResponse.cs
index e4addb6..b2308c5 100644
--- a/LibMatrix/Responses/SyncResponse.cs
+++ b/LibMatrix/Responses/SyncResponse.cs
@@ -39,7 +39,7 @@ public class SyncResponse {
     // supporting classes
     public class PresenceDataStructure {
         [JsonPropertyName("events")]
-        public List<StateEventResponse> Events { get; set; } = new();
+        public List<StateEventResponse>? Events { get; set; }
     }
 
     public class RoomsDataStructure {
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 2ec8571..8398ab9 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -431,6 +431,16 @@ public class GenericRoom {
 
         return await res.Content.ReadFromJsonAsync<T>();
     }
+    
+    public async Task<T?> GetRoomAccountDataOrNullAsync<T>(string key) {
+        try {
+            return await GetRoomAccountDataAsync<T>(key);
+        }
+        catch (MatrixException e) {
+            if (e.ErrorCode == "M_NOT_FOUND") return default;
+            throw;
+        }
+    }
 
     public async Task SetRoomAccountDataAsync(string key, object data) {
         var res = await Homeserver.ClientHttpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{Homeserver.UserId}/rooms/{RoomId}/account_data/{key}", data);
@@ -443,10 +453,17 @@ public class GenericRoom {
     public Task<StateEventResponse> GetEventAsync(string eventId) =>
         Homeserver.ClientHttpClient.GetFromJsonAsync<StateEventResponse>($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}");
 
-    public async Task<EventIdResponse> RedactEventAsync(string eventToRedact, string reason) {
+    public async Task<EventIdResponse> RedactEventAsync(string eventToRedact, string? reason = null) {
         var data = new { reason };
-        return (await (await Homeserver.ClientHttpClient.PutAsJsonAsync(
-            $"/_matrix/client/v3/rooms/{RoomId}/redact/{eventToRedact}/{Guid.NewGuid()}", data)).Content.ReadFromJsonAsync<EventIdResponse>())!;
+        var url = $"/_matrix/client/v3/rooms/{RoomId}/redact/{eventToRedact}/{Guid.NewGuid().ToString()}";
+        while (true) {
+            try {
+                return (await (await Homeserver.ClientHttpClient.PutAsJsonAsync(url, data)).Content.ReadFromJsonAsync<EventIdResponse>())!;
+            } catch (MatrixException e) {
+                if (e is { ErrorCode: MatrixException.ErrorCodes.M_FORBIDDEN }) throw;
+                throw;
+            }
+        }
     }
 
 #endregion
diff --git a/LibMatrix/Services/ServiceInstaller.cs b/LibMatrix/Services/ServiceInstaller.cs
index 06ea9de..8b7e54b 100644
--- a/LibMatrix/Services/ServiceInstaller.cs
+++ b/LibMatrix/Services/ServiceInstaller.cs
@@ -5,23 +5,13 @@ namespace LibMatrix.Services;
 
 public static class ServiceInstaller {
     public static IServiceCollection AddRoryLibMatrixServices(this IServiceCollection services, RoryLibMatrixConfiguration? config = null) {
-        //Check required services
-        // if (!services.Any(x => x.ServiceType == typeof(TieredStorageService)))
-        // throw new Exception("[RMUCore/DI] No TieredStorageService has been registered!");
         //Add config
         services.AddSingleton(config ?? new RoryLibMatrixConfiguration());
 
         //Add services
         services.AddSingleton<HomeserverResolverService>(sp => new HomeserverResolverService(sp.GetRequiredService<ILogger<HomeserverResolverService>>()));
-
-        // if (services.First(x => x.ServiceType == typeof(TieredStorageService)).Lifetime == ServiceLifetime.Singleton) {
         services.AddSingleton<HomeserverProviderService>();
-        // }
-        // else {
-        // services.AddScoped<HomeserverProviderService>();
-        // }
 
-        // services.AddScoped<MatrixHttpClient>();
         return services;
     }
 }
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index 8b44d2c..cc870e4 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -166,7 +166,7 @@ public class StateEventResponse : StateEvent {
     public string? Sender { get; set; }
 
     [JsonPropertyName("unsigned")]
-    public UnsignedData? Unsigned { get; set; }
+    public JsonObject? Unsigned { get; set; }
 
     [JsonPropertyName("event_id")]
     public string? EventId { get; set; }
diff --git a/Utilities/LibMatrix.TestDataGenerator/Program.cs b/Utilities/LibMatrix.TestDataGenerator/Program.cs
index 2583817..f3750a8 100644
--- a/Utilities/LibMatrix.TestDataGenerator/Program.cs
+++ b/Utilities/LibMatrix.TestDataGenerator/Program.cs
@@ -2,6 +2,7 @@
 
 using LibMatrix.Services;
 using LibMatrix.Utilities.Bot;
+using LibMatrix.Utilities.Bot.AppServices;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using TestDataGenerator.Bot;
diff --git a/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs
index afda89e..2cfcf32 100644
--- a/Utilities/LibMatrix.Utilities.Bot/AppServiceConfiguration.cs
+++ b/Utilities/LibMatrix.Utilities.Bot/AppServices/AppServiceConfiguration.cs
@@ -1,23 +1,47 @@
-namespace LibMatrix.Utilities.Bot;
+using System.Text.Json.Serialization;
+
+namespace LibMatrix.Utilities.Bot.AppServices;
 
 public class AppServiceConfiguration {
+    [JsonPropertyName("id")]
     public string Id { get; set; } = null!;
+
+    [JsonPropertyName("url")]
     public string? Url { get; set; } = null!;
+
+    [JsonPropertyName("sender_localpart")]
     public string SenderLocalpart { get; set; } = null!;
+
+    [JsonPropertyName("as_token")]
     public string AppserviceToken { get; set; } = null!;
+
+    [JsonPropertyName("hs_token")]
     public string HomeserverToken { get; set; } = null!;
+
+    [JsonPropertyName("protocols")]
     public List<string>? Protocols { get; set; } = null!;
+
+    [JsonPropertyName("rate_limited")]
     public bool? RateLimited { get; set; } = null!;
 
+    [JsonPropertyName("namespaces")]
     public AppserviceNamespaces Namespaces { get; set; } = null!;
 
     public class AppserviceNamespaces {
+        [JsonPropertyName("users")]
         public List<AppserviceNamespace>? Users { get; set; } = null;
+
+        [JsonPropertyName("aliases")]
         public List<AppserviceNamespace>? Aliases { get; set; } = null;
+
+        [JsonPropertyName("rooms")]
         public List<AppserviceNamespace>? Rooms { get; set; } = null;
 
         public class AppserviceNamespace {
+            [JsonPropertyName("exclusive")]
             public bool Exclusive { get; set; }
+
+            [JsonPropertyName("regex")]
             public string Regex { get; set; } = null!;
         }
     }
diff --git a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
index 621c1ee..ca6a4d8 100644
--- a/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
+++ b/Utilities/LibMatrix.Utilities.Bot/BotCommandInstaller.cs
@@ -1,8 +1,7 @@
 using ArcaneLibs;
-using LibMatrix.EventTypes.Spec.State;
 using LibMatrix.Homeservers;
-using LibMatrix.Responses;
 using LibMatrix.Services;
+using LibMatrix.Utilities.Bot.AppServices;
 using LibMatrix.Utilities.Bot.Interfaces;
 using LibMatrix.Utilities.Bot.Services;
 using Microsoft.Extensions.DependencyInjection;
@@ -22,6 +21,20 @@ public class BotInstaller(IServiceCollection services) {
         services.AddScoped<AuthenticatedHomeserverGeneric>(x => {
             var config = x.GetService<LibMatrixBotConfiguration>() ?? throw new Exception("No configuration found!");
             var hsProvider = x.GetService<HomeserverProviderService>() ?? throw new Exception("No homeserver provider found!");
+            
+            if (x.GetService<AppServiceConfiguration>() is AppServiceConfiguration appsvcConfig)
+                config.AccessToken = appsvcConfig.AppserviceToken;
+            else if (Environment.GetEnvironmentVariable("LIBMATRIX_ACCESS_TOKEN_PATH") is string path)
+                config.AccessTokenPath = path;
+            
+            if(string.IsNullOrWhiteSpace(config.AccessToken) && string.IsNullOrWhiteSpace(config.AccessTokenPath))
+                throw new Exception("Unable to add bot service without an access token or access token path!");
+            
+            if(!string.IsNullOrWhiteSpace(config.AccessTokenPath)) {
+                var token = File.ReadAllText(config.AccessTokenPath);
+                config.AccessToken = token;
+            }
+            
             var hs = hsProvider.GetAuthenticatedWithToken(config.Homeserver, config.AccessToken).Result;
 
             return hs;
diff --git a/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs b/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs
index 245442f..728b169 100644
--- a/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs
+++ b/Utilities/LibMatrix.Utilities.Bot/LibMatrixBotConfiguration.cs
@@ -6,7 +6,8 @@ namespace LibMatrix.Utilities.Bot;
 public class LibMatrixBotConfiguration {
     public LibMatrixBotConfiguration(IConfiguration config) => config.GetRequiredSection("LibMatrixBot").Bind(this);
     public string Homeserver { get; set; }
-    public string AccessToken { get; set; }
+    public string? AccessToken { get; set; }
+    public string? AccessTokenPath { get; set; }
     public List<string> Prefixes { get; set; }
     public bool MentionPrefix { get; set; }
     public string? LogRoom { get; set; }