diff options
-rw-r--r-- | LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs | 5 | ||||
-rw-r--r-- | LibMatrix/LibMatrixException.cs | 27 | ||||
-rw-r--r-- | LibMatrix/RoomTypes/GenericRoom.cs | 59 |
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 |