diff --git a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs b/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs
index 3727877..7563cac 100644
--- a/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs
+++ b/ExampleBots/ModerationBot/Commands/DbgDumpAllStateTypesCommand.cs
@@ -59,8 +59,8 @@ public class DbgDumpAllStateTypesCommand
string raw = "Count | State type | Mapped type", html = "<table><tr><th>Count</th><th>State type</th><th>Mapped type</th></tr>";
var groupedStates = states.GroupBy(x => x.Type).ToDictionary(x => x.Key, x => x.ToList()).OrderByDescending(x => x.Value.Count);
foreach (var (type, stateGroup) in groupedStates) {
- raw += $"{stateGroup.Count} | {type} | {stateGroup[0].GetType.Name}";
- html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].GetType.Name}</td></tr>";
+ raw += $"{stateGroup.Count} | {type} | {StateEvent.GetStateEventType(stateGroup[0].Type).Name}";
+ html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{StateEvent.GetStateEventType(stateGroup[0].Type).Name}</td></tr>";
}
html += "</table>";
diff --git a/ExampleBots/ModerationBot/ModerationBot.cs b/ExampleBots/ModerationBot/ModerationBot.cs
index 1be7bd5..7c95229 100644
--- a/ExampleBots/ModerationBot/ModerationBot.cs
+++ b/ExampleBots/ModerationBot/ModerationBot.cs
@@ -70,7 +70,7 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () => {
while (!cancellationToken.IsCancellationRequested) {
- var controlRoomMembers = _controlRoom.GetMembersAsync();
+ var controlRoomMembers = _controlRoom.GetMembersEnumerableAsync();
var pls = await _controlRoom.GetPowerLevelsAsync();
await foreach (var member in controlRoomMembers) {
if ((member.TypedContent as RoomMemberEventContent)?
@@ -110,8 +110,8 @@ public class ModerationBot(AuthenticatedHomeserverGeneric hs, ILogger<Moderation
"Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: true, ignoreNull: true));
if (@event != null && (
- @event.GetType.IsAssignableTo(typeof(BasePolicy))
- || @event.GetType.IsAssignableTo(typeof(PolicyRuleEventContent))
+ @event.MappedType.IsAssignableTo(typeof(BasePolicy))
+ || @event.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))
))
await engine.ReloadActivePolicyListById(@event.RoomId);
diff --git a/ExampleBots/ModerationBot/PolicyEngine.cs b/ExampleBots/ModerationBot/PolicyEngine.cs
index 5af99ac..114b90d 100644
--- a/ExampleBots/ModerationBot/PolicyEngine.cs
+++ b/ExampleBots/ModerationBot/PolicyEngine.cs
@@ -85,8 +85,8 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
var stateEvents = room.GetFullStateAsync();
await foreach (var stateEvent in stateEvents) {
if (stateEvent != null && (
- stateEvent.GetType.IsAssignableTo(typeof(BasePolicy))
- || stateEvent.GetType.IsAssignableTo(typeof(PolicyRuleEventContent))
+ stateEvent.MappedType.IsAssignableTo(typeof(BasePolicy))
+ || stateEvent.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))
)) {
policyList.Policies.Add(stateEvent);
}
@@ -253,8 +253,8 @@ public class PolicyEngine(AuthenticatedHomeserverGeneric hs, ILogger<ModerationB
string raw = "Count | State type | Mapped type", html = "<table><tr><th>Count</th><th>State type</th><th>Mapped type</th></tr>";
var groupedStates = states.GroupBy(x => x.Type).ToDictionary(x => x.Key, x => x.ToList()).OrderByDescending(x => x.Value.Count);
foreach (var (type, stateGroup) in groupedStates) {
- raw += $"{stateGroup.Count} | {type} | {stateGroup[0].GetType.Name}";
- html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].GetType.Name}</td></tr>";
+ raw += $"{stateGroup.Count} | {type} | {stateGroup[0].MappedType.Name}";
+ html += $"<tr><td>{stateGroup.Count}</td><td>{type}</td><td>{stateGroup[0].MappedType.Name}</td></tr>";
}
html += "</table>";
diff --git a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs
index 6d2cb3a..0bb3265 100644
--- a/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs
+++ b/ExampleBots/PluralContactBotPoC/Bot/Commands/CreateSystemCommand.cs
@@ -35,7 +35,7 @@ public class CreateSystemCommand(IServiceProvider services, HomeserverProviderSe
Members = new(),
};
- var state = ctx.Room.GetMembersAsync();
+ var state = ctx.Room.GetMembersEnumerableAsync();
await foreach (var member in state) {
sysData.Members.Add(member.StateKey);
}
diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs
index 17cc31d..71bb0e5 100644
--- a/LibMatrix/Extensions/HttpClientExtensions.cs
+++ b/LibMatrix/Extensions/HttpClientExtensions.cs
@@ -37,7 +37,7 @@ public class MatrixHttpClient : HttpClient {
return options;
}
- public override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null");
if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri);
// if (AssertedUserId is not null) request.RequestUri = request.RequestUri.AddQuery("user_id", AssertedUserId);
@@ -58,15 +58,18 @@ public class MatrixHttpClient : HttpClient {
}
HttpResponseMessage responseMessage;
- try {
+ // try {
responseMessage = await base.SendAsync(request, cancellationToken);
- }
- catch (Exception e) {
- typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)
- ?.SetValue(request, 0);
- await Task.Delay(2500, cancellationToken);
- return await SendAsync(request, cancellationToken);
- }
+ // }
+ // catch (Exception e) {
+ // if (requestSettings is { Retries: 0 }) throw;
+ // typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance)
+ // ?.SetValue(request, 0);
+ // await Task.Delay(requestSettings?.RetryDelay ?? 2500, cancellationToken);
+ // if(requestSettings is not null) requestSettings.Retries--;
+ // return await SendAsync(request, cancellationToken);
+ // throw;
+ // }
if (responseMessage.IsSuccessStatusCode) return responseMessage;
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index ba42735..0a11849 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -1,5 +1,7 @@
using System.Diagnostics;
using System.Net.Http.Json;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using ArcaneLibs.Extensions;
using LibMatrix.Filters;
using LibMatrix.Homeservers;
@@ -28,8 +30,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
Console.WriteLine("Homeserver for SyncHelper is not properly configured!");
throw new ArgumentNullException(nameof(homeserver.ClientHttpClient), "Null passed as homeserver for SyncHelper!");
}
-
-
+
var sw = Stopwatch.StartNew();
var url = $"/_matrix/client/v3/sync?timeout={Timeout}&set_presence={SetPresence}&full_state={(FullState ? "true" : "false")}";
@@ -42,7 +43,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
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);
var deserializeSw = Stopwatch.StartNew();
- var resp = await httpResp.Content.ReadFromJsonAsync<SyncResponse>(cancellationToken: cancellationToken ?? CancellationToken.None);
+ 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);
var timeToWait = MinimumDelay.Subtract(sw.Elapsed);
if (timeToWait.TotalMilliseconds > 0)
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index c1d6461..6fcd8e8 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -16,37 +16,41 @@ using LibMatrix.Services;
namespace LibMatrix.Homeservers;
public class AuthenticatedHomeserverGeneric(string serverName, string accessToken) : RemoteHomeserver(serverName) {
- public static async Task<T> Create<T>(string serverName, string accessToken, string? proxy = null) where T : AuthenticatedHomeserverGeneric {
- var instance = Activator.CreateInstance(typeof(T), serverName, accessToken) as T
- ?? throw new InvalidOperationException($"Failed to create instance of {typeof(T).Name}");
- HomeserverResolverService.WellKnownUris? urls = null;
- if (proxy is null)
- urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(serverName);
-
+ public static async Task<T> Create<T>(string serverName, string accessToken, string? proxy = null) where T : AuthenticatedHomeserverGeneric =>
+ await Create(typeof(T), serverName, accessToken, proxy) as T ?? throw new InvalidOperationException($"Failed to create instance of {typeof(T).Name}");
+ public static async Task<AuthenticatedHomeserverGeneric> Create(Type type, string serverName, string accessToken, string? proxy = null) {
+ if(!type.IsAssignableTo(typeof(AuthenticatedHomeserverGeneric))) throw new ArgumentException("Type must be a subclass of AuthenticatedHomeserverGeneric", nameof(type));
+ var instance = Activator.CreateInstance(type, serverName, accessToken) as AuthenticatedHomeserverGeneric
+ ?? throw new InvalidOperationException($"Failed to create instance of {type.Name}");
+
instance.ClientHttpClient = new() {
- BaseAddress = new Uri(proxy ?? urls?.Client
- ?? throw new InvalidOperationException("Failed to resolve homeserver")),
Timeout = TimeSpan.FromMinutes(15),
DefaultRequestHeaders = {
Authorization = new AuthenticationHeaderValue("Bearer", accessToken)
}
};
instance.ServerHttpClient = new() {
- BaseAddress = new Uri(proxy ?? urls?.Server
- ?? throw new InvalidOperationException("Failed to resolve homeserver")),
Timeout = TimeSpan.FromMinutes(15),
DefaultRequestHeaders = {
Authorization = new AuthenticationHeaderValue("Bearer", accessToken)
}
};
- instance.WhoAmI = await instance.ClientHttpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami");
-
- if (proxy is not null) {
+ if (string.IsNullOrWhiteSpace(proxy)) {
+ HomeserverResolverService.WellKnownUris? urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(serverName);
+ instance.ClientHttpClient.BaseAddress = new Uri(urls?.Client ?? throw new InvalidOperationException("Failed to resolve homeserver"));
+ instance.ServerHttpClient.BaseAddress = new Uri(urls?.Server ?? throw new InvalidOperationException("Failed to resolve homeserver"));
+ }
+ else {
+ instance.ClientHttpClient.BaseAddress = new Uri(proxy);
+ instance.ServerHttpClient.BaseAddress = new Uri(proxy);
instance.ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName);
instance.ServerHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName);
}
+ instance.WhoAmI = await instance.ClientHttpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami");
+
+
return instance;
}
@@ -55,12 +59,6 @@ public class AuthenticatedHomeserverGeneric(string serverName, string accessToke
public string UserLocalpart => UserId.Split(":")[0][1..];
public string ServerName => UserId.Split(":", 2)[1];
- // public virtual async Task<WhoAmIResponse> WhoAmI() {
- // if (_whoAmI is not null) return _whoAmI;
- // _whoAmI = await _httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami");
- // return _whoAmI;
- // }
-
public string AccessToken { get; set; } = accessToken;
public GenericRoom GetRoom(string roomId) {
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index a47b731..a461d6e 100644
--- a/LibMatrix/Homeservers/RemoteHomeServer.cs
+++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -37,9 +37,9 @@ public class RemoteHomeserver(string baseUrl) {
public MatrixHttpClient ServerHttpClient { get; set; } = null!;
public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } = null!;
- public async Task<UserProfileResponse> GetProfileAsync(string mxid) {
+ public async Task<UserProfileResponse> GetProfileAsync(string mxid, bool useCache = false) {
if (mxid is null) throw new ArgumentNullException(nameof(mxid));
- if (_profileCache.TryGetValue(mxid, out var value)) {
+ if (useCache && _profileCache.TryGetValue(mxid, out var value)) {
if (value is SemaphoreSlim s) await s.WaitAsync();
if (value is UserProfileResponse p) return p;
}
diff --git a/LibMatrix/Responses/SyncResponse.cs b/LibMatrix/Responses/SyncResponse.cs
index 42759ff..49259d0 100644
--- a/LibMatrix/Responses/SyncResponse.cs
+++ b/LibMatrix/Responses/SyncResponse.cs
@@ -2,6 +2,10 @@ using System.Text.Json.Serialization;
namespace LibMatrix.Responses;
+[JsonSourceGenerationOptions(WriteIndented = true)]
+[JsonSerializable(typeof(SyncResponse))]
+internal partial class SyncResponseSerializerContext : JsonSerializerContext { }
+
public class SyncResponse {
[JsonPropertyName("next_batch")]
public string NextBatch { get; set; } = null!;
@@ -114,4 +118,4 @@ public class SyncResponse {
public EventList? InviteState { get; set; }
}
}
-}
+}
\ No newline at end of file
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 9e2cb67..bfb7f43 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -184,7 +184,7 @@ public class GenericRoom {
else sw.Restart();
foreach (var resp in result.Chunk) {
if (resp?.Type != "m.room.member") continue;
- if (joinedOnly && (resp.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue;
+ if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue;
yield return resp;
}
@@ -209,7 +209,7 @@ public class GenericRoom {
var members = new List<StateEventResponse>();
foreach (var resp in result.Chunk) {
if (resp?.Type != "m.room.member") continue;
- if (joinedOnly && (resp.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue;
+ if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue;
members.Add(resp);
}
@@ -267,7 +267,7 @@ public class GenericRoom {
var memberList = new List<string>();
int memberCount = 0;
await foreach (var member in members)
- memberList.Add((member.TypedContent is RoomMemberEventContent memberEvent ? memberEvent.DisplayName : "") ?? "");
+ memberList.Add(member.RawContent?["displayname"]?.GetValue<string>() ?? "");
memberCount = memberList.Count;
memberList.RemoveAll(string.IsNullOrWhiteSpace);
memberList = memberList.OrderBy(x => x).ToList();
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index dc4acb1..577a706 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -25,22 +25,34 @@ public class HomeserverProviderService(ILogger<HomeserverProviderService> logger
}
}
- // var domain = proxy ?? (await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver)).client;
-
var rhs = await RemoteHomeserver.Create(homeserver, proxy);
- var clientVersions = await rhs.GetClientVersionsAsync();
+ ClientVersionsResponse clientVersions = new();
+ try {
+ clientVersions = await rhs.GetClientVersionsAsync();
+ }
+ catch (Exception e) {
+ logger.LogError(e, "Failed to get client versions for {homeserver}", homeserver);
+ }
+
if (proxy is not null)
- logger.LogInformation($"Homeserver {homeserver} proxied via {proxy}...");
- logger.LogInformation($"{homeserver}: " + clientVersions.ToJson());
+ logger.LogInformation("Homeserver {homeserver} proxied via {proxy}...", homeserver, proxy);
+ logger.LogInformation("{homeserver}: {clientVersions}", homeserver, clientVersions.ToJson());
- if (clientVersions.UnstableFeatures.TryGetValue("gay.rory.mxapiextensions.v0", out bool a) && a)
- hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverMxApiExtended>(homeserver, accessToken, proxy);
- else {
- var serverVersion = await rhs.GetServerVersionAsync();
- if (serverVersion is { Server.Name: "Synapse" })
- hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverSynapse>(homeserver, accessToken, proxy);
- else
- hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(homeserver, accessToken, proxy);
+ try {
+ if (clientVersions.UnstableFeatures.TryGetValue("gay.rory.mxapiextensions.v0", out bool a) && a)
+ hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverMxApiExtended>(homeserver, accessToken, proxy);
+ else {
+ var serverVersion = await rhs.GetServerVersionAsync();
+ if (serverVersion is { Server.Name: "Synapse" })
+ hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverSynapse>(homeserver, accessToken, proxy);
+ else
+ hs = await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(homeserver, accessToken, proxy);
+ }
+ }
+ catch (Exception e) {
+ logger.LogError(e, "Failed to create authenticated homeserver for {homeserver}", homeserver);
+ sem.Release();
+ throw;
}
if(impersonatedMxid is not null)
diff --git a/LibMatrix/Services/ServiceInstaller.cs b/LibMatrix/Services/ServiceInstaller.cs
index b1c98e1..ad5bedc 100644
--- a/LibMatrix/Services/ServiceInstaller.cs
+++ b/LibMatrix/Services/ServiceInstaller.cs
@@ -6,20 +6,20 @@ 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("[MRUCore/DI] No TieredStorageService has been registered!");
+ // if (!services.Any(x => x.ServiceType == typeof(TieredStorageService)))
+ // throw new Exception("[MRUCore/DI] No TieredStorageService has been registered!");
//Add config
services.AddSingleton(config ?? new RoryLibMatrixConfiguration());
//Add services
services.AddSingleton<HomeserverResolverService>();
- if (services.First(x => x.ServiceType == typeof(TieredStorageService)).Lifetime == ServiceLifetime.Singleton) {
+ // if (services.First(x => x.ServiceType == typeof(TieredStorageService)).Lifetime == ServiceLifetime.Singleton) {
services.AddSingleton<HomeserverProviderService>();
- }
- else {
- services.AddScoped<HomeserverProviderService>();
- }
+ // }
+ // else {
+ // services.AddScoped<HomeserverProviderService>();
+ // }
// services.AddScoped<MatrixHttpClient>();
return services;
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index ad7605a..4a0adbd 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -26,8 +26,11 @@ public class StateEvent {
}).ToFrozenDictionary();
public static Type GetStateEventType(string type) => KnownStateEventTypesByName.GetValueOrDefault(type) ?? typeof(UnknownEventContent);
+
+ [JsonIgnore]
+ public Type MappedType => GetStateEventType(Type);
- private static readonly JsonSerializerOptions TypedContentSerializerOptions = new JsonSerializerOptions() {
+ private static readonly JsonSerializerOptions TypedContentSerializerOptions = new() {
Converters = {
new JsonFloatStringConverter(),
new JsonDoubleStringConverter(),
diff --git a/Tests/LibMatrix.Tests/Tests/RoomTests.cs b/Tests/LibMatrix.Tests/Tests/RoomTests.cs
index 913e044..bec8d18 100644
--- a/Tests/LibMatrix.Tests/Tests/RoomTests.cs
+++ b/Tests/LibMatrix.Tests/Tests/RoomTests.cs
@@ -52,7 +52,7 @@ public class RoomTests : TestBed<TestFixture> {
var hs = await HomeserverAbstraction.GetHomeserver();
var room = await RoomAbstraction.GetTestRoom(hs);
Assert.NotNull(room);
- var members = room.GetMembersAsync();
+ var members = room.GetMembersEnumerableAsync();
Assert.NotNull(members);
bool hitMembers = false;
await foreach (var member in members) {
@@ -248,12 +248,7 @@ public class RoomTests : TestBed<TestFixture> {
}
await Task.WhenAll(tasks);
- var states = room.GetMembersAsync(false);
- var count = 0;
- await foreach (var state in states) {
- count++;
- }
- // Assert.Equal(++expectedCount, count);
- Assert.Equal(16, count);
+ var states = await room.GetMembersListAsync(false);
+ Assert.Equal(16, states.Count);
}
}
|