diff --git a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
index 852e1d8..060867d 100644
--- a/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
+++ b/MatrixRoomUtils.Core/Extensions/HttpClientExtensions.cs
@@ -1,3 +1,4 @@
+using System.Net.Http.Headers;
using System.Reflection;
using System.Text.Json;
@@ -19,6 +20,7 @@ public static class HttpClientExtensions {
public class MatrixHttpClient : HttpClient {
public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ Console.WriteLine($"Sending request to {request.RequestUri}");
try
{
HttpRequestOptionsKey<bool> WebAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse");
@@ -35,10 +37,10 @@ public class MatrixHttpClient : HttpClient {
}
var a = await base.SendAsync(request, cancellationToken);
if (!a.IsSuccessStatusCode) {
- Console.WriteLine($"Failed to send request: {a.StatusCode}");
var content = await a.Content.ReadAsStringAsync(cancellationToken);
if (content.StartsWith('{')) {
var ex = JsonSerializer.Deserialize<MatrixException>(content);
+ Console.WriteLine($"Failed to send request: {ex}");
if (ex?.RetryAfterMs is not null) {
await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)?.SetValue(request, 0);
@@ -50,4 +52,13 @@ public class MatrixHttpClient : HttpClient {
}
return a;
}
+ // GetFromJsonAsync
+ public async Task<T> GetFromJsonAsync<T>(string requestUri, CancellationToken cancellationToken = default) {
+ var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ var response = await SendAsync(request, cancellationToken);
+ response.EnsureSuccessStatusCode();
+ await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ return await JsonSerializer.DeserializeAsync<T>(responseStream, cancellationToken: cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/MatrixException.cs b/MatrixRoomUtils.Core/MatrixException.cs
index 50fae20..4795d6d 100644
--- a/MatrixRoomUtils.Core/MatrixException.cs
+++ b/MatrixRoomUtils.Core/MatrixException.cs
@@ -17,41 +17,41 @@ public class MatrixException : Exception {
public int? RetryAfterMs { get; set; }
public override string Message =>
- ErrorCode switch {
+ $"{ErrorCode}: {ErrorCode switch {
// common
- "M_FORBIDDEN" => "You do not have permission to perform this action: " + Error,
- "M_UNKNOWN_TOKEN" => "The access token specified was not recognised: " + Error + (SoftLogout == true ? " (soft logout)" : ""),
- "M_MISSING_TOKEN" => "No access token was specified: " + Error,
- "M_BAD_JSON" => "Request contained valid JSON, but it was malformed in some way: " + Error,
- "M_NOT_JSON" => "Request did not contain valid JSON: " + Error,
- "M_NOT_FOUND" => "The requested resource was not found: " + Error,
- "M_LIMIT_EXCEEDED" => "Too many requests have been sent in a short period of time. Wait a while then try again: " + Error,
- "M_UNRECOGNISED" => "The server did not recognise the request: " + Error,
- "M_UNKOWN" => "The server encountered an unexpected error: " + Error,
+ "M_FORBIDDEN" => $"You do not have permission to perform this action: {Error}",
+ "M_UNKNOWN_TOKEN" => $"The access token specified was not recognised: {Error}{(SoftLogout == true ? " (soft logout)" : "")}",
+ "M_MISSING_TOKEN" => $"No access token was specified: {Error}",
+ "M_BAD_JSON" => $"Request contained valid JSON, but it was malformed in some way: {Error}",
+ "M_NOT_JSON" => $"Request did not contain valid JSON: {Error}",
+ "M_NOT_FOUND" => $"The requested resource was not found: {Error}",
+ "M_LIMIT_EXCEEDED" => $"Too many requests have been sent in a short period of time. Wait a while then try again: {Error}",
+ "M_UNRECOGNISED" => $"The server did not recognise the request: {Error}",
+ "M_UNKOWN" => $"The server encountered an unexpected error: {Error}",
// endpoint specific
- "M_UNAUTHORIZED" => "The request did not contain valid authentication information for the target of the request: " + Error,
- "M_USER_DEACTIVATED" => "The user ID associated with the request has been deactivated: " + Error,
- "M_USER_IN_USE" => "The user ID associated with the request is already in use: " + Error,
- "M_INVALID_USERNAME" => "The requested user ID is not valid: " + Error,
- "M_ROOM_IN_USE" => "The room alias requested is already taken: " + Error,
- "M_INVALID_ROOM_STATE" => "The room associated with the request is not in a valid state to perform the request: " + Error,
- "M_THREEPID_IN_USE" => "The threepid requested is already associated with a user ID on this server: " + Error,
- "M_THREEPID_NOT_FOUND" => "The threepid requested is not associated with any user ID: " + Error,
- "M_THREEPID_AUTH_FAILED" => "The provided threepid and/or token was invalid: " + Error,
- "M_THREEPID_DENIED" => "The homeserver does not permit the third party identifier in question: " + Error,
- "M_SERVER_NOT_TRUSTED" => "The homeserver does not trust the identity server: " + Error,
- "M_UNSUPPORTED_ROOM_VERSION" => "The room version is not supported: " + Error,
- "M_INCOMPATIBLE_ROOM_VERSION" => "The room version is incompatible: " + Error,
- "M_BAD_STATE" => "The request was invalid because the state was invalid: " + Error,
- "M_GUEST_ACCESS_FORBIDDEN" => "Guest access is forbidden: " + Error,
- "M_CAPTCHA_NEEDED" => "Captcha needed: " + Error,
- "M_CAPTCHA_INVALID" => "Captcha invalid: " + Error,
- "M_MISSING_PARAM" => "Missing parameter: " + Error,
- "M_INVALID_PARAM" => "Invalid parameter: " + Error,
- "M_TOO_LARGE" => "The request or entity was too large: " + Error,
- "M_EXCLUSIVE" => "The resource being requested is reserved by an application service, or the application service making the request has not created the resource: " + Error,
- "M_RESOURCE_LIMIT_EXCEEDED" => "Exceeded resource limit: " + Error,
- "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => "Cannot leave server notice room: " + Error,
- _ => "Unknown error: " + new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)
- };
+ "M_UNAUTHORIZED" => $"The request did not contain valid authentication information for the target of the request: {Error}",
+ "M_USER_DEACTIVATED" => $"The user ID associated with the request has been deactivated: {Error}",
+ "M_USER_IN_USE" => $"The user ID associated with the request is already in use: {Error}",
+ "M_INVALID_USERNAME" => $"The requested user ID is not valid: {Error}",
+ "M_ROOM_IN_USE" => $"The room alias requested is already taken: {Error}",
+ "M_INVALID_ROOM_STATE" => $"The room associated with the request is not in a valid state to perform the request: {Error}",
+ "M_THREEPID_IN_USE" => $"The threepid requested is already associated with a user ID on this server: {Error}",
+ "M_THREEPID_NOT_FOUND" => $"The threepid requested is not associated with any user ID: {Error}",
+ "M_THREEPID_AUTH_FAILED" => $"The provided threepid and/or token was invalid: {Error}",
+ "M_THREEPID_DENIED" => $"The homeserver does not permit the third party identifier in question: {Error}",
+ "M_SERVER_NOT_TRUSTED" => $"The homeserver does not trust the identity server: {Error}",
+ "M_UNSUPPORTED_ROOM_VERSION" => $"The room version is not supported: {Error}",
+ "M_INCOMPATIBLE_ROOM_VERSION" => $"The room version is incompatible: {Error}",
+ "M_BAD_STATE" => $"The request was invalid because the state was invalid: {Error}",
+ "M_GUEST_ACCESS_FORBIDDEN" => $"Guest access is forbidden: {Error}",
+ "M_CAPTCHA_NEEDED" => $"Captcha needed: {Error}",
+ "M_CAPTCHA_INVALID" => $"Captcha invalid: {Error}",
+ "M_MISSING_PARAM" => $"Missing parameter: {Error}",
+ "M_INVALID_PARAM" => $"Invalid parameter: {Error}",
+ "M_TOO_LARGE" => $"The request or entity was too large: {Error}",
+ "M_EXCLUSIVE" => $"The resource being requested is reserved by an application service, or the application service making the request has not created the resource: {Error}",
+ "M_RESOURCE_LIMIT_EXCEEDED" => $"Exceeded resource limit: {Error}",
+ "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
+ _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
+ }}";
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
index f57c855..879ae6b 100644
--- a/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
+++ b/MatrixRoomUtils.Core/RoomTypes/GenericRoom.cs
@@ -44,7 +44,17 @@ public class GenericRoom {
var url = $"/_matrix/client/v3/rooms/{RoomId}/state";
if (!string.IsNullOrEmpty(type)) url += $"/{type}";
if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}";
- return await _httpClient.GetFromJsonAsync<T>(url);
+ try {
+ var resp = await _httpClient.GetFromJsonAsync<T>(url);
+ return resp;
+ }
+ catch (MatrixException e) {
+ if (e is not { ErrorCode: "M_NOT_FOUND" }) {
+ throw;
+ }
+ Console.WriteLine(e);
+ return default;
+ }
}
public async Task<MessagesResponse> GetMessagesAsync(string from = "", int limit = 10, string dir = "b",
@@ -56,8 +66,13 @@ public class GenericRoom {
}
public async Task<string> GetNameAsync() {
- var res = await GetStateAsync<RoomNameEventData>("m.room.name");
- return res.Name ?? RoomId;
+ try {
+ var res = await GetStateAsync<RoomNameEventData>("m.room.name");
+ return res?.Name ?? RoomId;
+ }
+ catch (MatrixException e) {
+ return $"{RoomId} ({e.ErrorCode})";
+ }
}
public async Task JoinAsync(string[]? homeservers = null, string? reason = null) {
diff --git a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
index 3be3130..1b93064 100644
--- a/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
+++ b/MatrixRoomUtils.Core/RoomTypes/SpaceRoom.cs
@@ -13,15 +13,16 @@ public class SpaceRoom : GenericRoom {
_homeServer = homeServer;
}
- public async Task<List<GenericRoom>> GetRoomsAsync(bool includeRemoved = false) {
+ private static SemaphoreSlim _semaphore = new(1, 1);
+ public async IAsyncEnumerable<GenericRoom> GetRoomsAsync(bool includeRemoved = false) {
+ await _semaphore.WaitAsync();
var rooms = new List<GenericRoom>();
- var state = GetFullStateAsync().ToBlockingEnumerable().ToList();
- var childStates = state.Where(x => x.Type == "m.space.child");
- foreach (var stateEvent in childStates) {
- if (stateEvent.TypedContent.ToJson() != "{}" || includeRemoved)
- rooms.Add(await _homeServer.GetRoom(stateEvent.StateKey));
+ var state = GetFullStateAsync();
+ await foreach (var stateEvent in state) {
+ if (stateEvent.Type != "m.space.child") continue;
+ if (stateEvent.RawContent.ToJson() != "{}" || includeRemoved)
+ yield return await _homeServer.GetRoom(stateEvent.StateKey);
}
-
- return rooms;
+ _semaphore.Release();
}
}
\ No newline at end of file
diff --git a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
index 870e0d4..b2ea987 100644
--- a/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
+++ b/MatrixRoomUtils.Core/Services/HomeserverProviderService.cs
@@ -30,9 +30,9 @@ public class HomeserverProviderService {
await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
hs._httpClient.Dispose();
hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
- hs._httpClient.Timeout = TimeSpan.FromSeconds(5);
+ hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
hs._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
-
+
hs.WhoAmI = (await hs._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!;
return hs;
}
@@ -43,7 +43,7 @@ public class HomeserverProviderService {
await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
hs._httpClient.Dispose();
hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
- hs._httpClient.Timeout = TimeSpan.FromSeconds(5);
+ hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
return hs;
}
|