about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2024-04-25 06:31:11 +0200
committerRory& <root@rory.gay>2024-04-25 06:31:11 +0200
commit5ca0a45606ad2ca7e1ca45a3b27be08e9640dd9d (patch)
tree6a04296d6fd93cb090b5d91ae9ca44ef45c7e34e
parentPartial User-Interactive Authentication, allow skipping homeserver typing (diff)
downloadLibMatrix-5ca0a45606ad2ca7e1ca45a3b27be08e9640dd9d.tar.xz
Fixes
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs5
-rw-r--r--LibMatrix/LibMatrixException.cs27
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs59
3 files changed, 86 insertions, 5 deletions
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index 267b54d..5520d3a 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -125,12 +125,15 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
 
 #region Utility Functions
 
-    public virtual async IAsyncEnumerable<GenericRoom> GetJoinedRoomsByType(string type) {
+    public virtual async IAsyncEnumerable<GenericRoom> GetJoinedRoomsByType(string type, int? semaphoreCount = null) {
         var rooms = await GetJoinedRooms();
+        SemaphoreSlim? semaphoreSlim = semaphoreCount is null ? null : new(semaphoreCount.Value, semaphoreCount.Value);
         var tasks = rooms.Select(async room => {
             while (true) {
+                if (semaphoreSlim is not null) await semaphoreSlim.WaitAsync();
                 try {
                     var roomType = await room.GetRoomType();
+                    if (semaphoreSlim is not null) semaphoreSlim.Release();
                     if (roomType == type) return room;
                     return null;
                 }
diff --git a/LibMatrix/LibMatrixException.cs b/LibMatrix/LibMatrixException.cs
new file mode 100644
index 0000000..5854826
--- /dev/null
+++ b/LibMatrix/LibMatrixException.cs
@@ -0,0 +1,27 @@
+using System.Text.Json.Serialization;
+using ArcaneLibs.Extensions;
+
+namespace LibMatrix;
+
+public class LibMatrixException : Exception {
+    [JsonPropertyName("errcode")]
+    public required string ErrorCode { get; set; }
+
+    [JsonPropertyName("error")]
+    public required string Error { get; set; }
+
+
+    public object GetAsObject() => new { errcode = ErrorCode, error = Error };
+    public string GetAsJson() => GetAsObject().ToJson(ignoreNull: true);
+
+    public override string Message =>
+        $"{ErrorCode}: {ErrorCode switch {
+            "M_UNSUPPORTED" => "The requested feature is not supported",
+            _ => $"Unknown error: {GetAsObject().ToJson(ignoreNull: true)}"
+        }}\nError: {Error}";
+
+    public static class ErrorCodes {
+        public const string M_NOT_FOUND = "M_NOT_FOUND";
+        public const string M_UNSUPPORTED = "M_UNSUPPORTED";
+    }
+}
\ No newline at end of file
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index e4d2b9c..43f3acc 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -10,6 +10,8 @@ using LibMatrix.EventTypes;
 using LibMatrix.EventTypes.Spec;
 using LibMatrix.EventTypes.Spec.State;
 using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Filters;
+using LibMatrix.Helpers;
 using LibMatrix.Homeservers;
 using LibMatrix.Services;
 using Microsoft.Extensions.Logging.Abstractions;
@@ -74,7 +76,11 @@ public class GenericRoom {
         try {
             var resp = await Homeserver.ClientHttpClient.GetFromJsonAsync<JsonObject>(url);
             if (resp["type"]?.GetValue<string>() != type)
-                throw new InvalidDataException("Returned event type does not match requested type, or server does not support passing `format`.");
+                throw new LibMatrixException() {
+                    Error = "Homeserver returned event type does not match requested type, or server does not support passing `format`.",
+                    ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED
+                };
+            // throw new InvalidDataException("Returned event type does not match requested type, or server does not support passing `format`.");
             return resp.Deserialize<StateEventResponse>();
         }
         catch (MatrixException e) {
@@ -87,6 +93,45 @@ public class GenericRoom {
         }
     }
 
+    public async Task<string?> GetStateEventIdAsync(string type, string stateKey = "", bool fallbackToSync = true) {
+        try {
+            return (await GetStateEventAsync(type, stateKey)).EventId ?? throw new LibMatrixException() {
+                ErrorCode = LibMatrixException.ErrorCodes.M_UNSUPPORTED,
+                Error = "Homeserver does not include event ID in state events."
+            };
+        }
+        catch (LibMatrixException e) {
+            if (e.ErrorCode == LibMatrixException.ErrorCodes.M_UNSUPPORTED) {
+                if (!fallbackToSync) throw;
+                Console.WriteLine("WARNING: Homeserver does not support getting event ID from state events, falling back to sync");
+                var sh = new SyncHelper(Homeserver);
+                var emptyFilter = new SyncFilter.EventFilter(types: [], limit: 1, senders: [], notTypes: ["*"]);
+                var emptyStateFilter = new SyncFilter.RoomFilter.StateFilter(types: [], limit: 1, senders: [], notTypes: ["*"], rooms:[]);
+                sh.Filter = new() {
+                    Presence = emptyFilter,
+                    AccountData = emptyFilter,
+                    Room = new SyncFilter.RoomFilter() {
+                        AccountData = emptyStateFilter,
+                        Timeline = emptyStateFilter,
+                        Ephemeral = emptyStateFilter,
+                        State = new SyncFilter.RoomFilter.StateFilter(),
+                        Rooms = [RoomId]
+                    }
+                };
+                var sync = await sh.SyncAsync();
+                var state = sync.Rooms.Join[RoomId].State.Events;
+                var stateEvent = state.FirstOrDefault(x => x.Type == type && x.StateKey == stateKey);
+                if (stateEvent is null) throw new LibMatrixException() {
+                    ErrorCode = LibMatrixException.ErrorCodes.M_NOT_FOUND,
+                    Error = "State event not found in sync response"
+                };
+                return stateEvent.EventId;
+            }
+
+            return null;
+        }
+    }
+
     public async Task<StateEventResponse?> GetStateEventOrNullAsync(string type, string stateKey = "") {
         try {
             return await GetStateEventAsync(type, stateKey);
@@ -174,7 +219,7 @@ public class GenericRoom {
 
         var joinUrl = $"/_matrix/client/v3/join/{HttpUtility.UrlEncode(RoomId)}";
         Console.WriteLine($"Calling {joinUrl} with {homeservers?.Length ?? 0} via's...");
-        if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':')[1] };
+        if (homeservers == null || homeservers.Length == 0) homeservers = new[] { RoomId.Split(':', 2)[1] };
         var fullJoinUrl = $"{joinUrl}?server_name=" + string.Join("&server_name=", homeservers);
         var res = await Homeserver.ClientHttpClient.PostAsJsonAsync(fullJoinUrl, new {
             reason
@@ -406,7 +451,8 @@ public class GenericRoom {
         }
     }
 
-    public Task<StateEventResponse> GetEventAsync(string eventId) => Homeserver.ClientHttpClient.GetFromJsonAsync<StateEventResponse>($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}");
+    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) {
         var data = new { reason };
@@ -457,8 +503,13 @@ public class GenericRoom {
                 }
 
             if (stateTypeIgnore.Contains(state.Type)) continue;
-            await SendStateEventAsync(state.Type, state.StateKey, new object());
+            try {
+                await SendStateEventAsync(state.Type, state.StateKey, new object());
+            }
+            catch { }
         }
+
+        await LeaveAsync("Disbanded room");
     }
 
 #endregion