diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index 5fd3311..4185353 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -295,7 +295,19 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
public async Task<RoomIdResponse> JoinRoomAsync(string roomId, List<string> homeservers = null, string? reason = null) {
var joinUrl = $"/_matrix/client/v3/join/{HttpUtility.UrlEncode(roomId)}";
Console.WriteLine($"Calling {joinUrl} with {homeservers?.Count ?? 0} via's...");
- if (homeservers == null || homeservers.Count == 0) homeservers = new List<string> { roomId.Split(':')[1] };
+ if (homeservers is not { Count: > 0 }) {
+ // Legacy room IDs: !abc:server.xyz
+ if (roomId.Contains(':'))
+ homeservers = [ServerName, roomId.Split(':')[1]];
+ // v12+ room IDs: !<hash>
+ else {
+ homeservers = [ServerName];
+ foreach (var room in await GetJoinedRooms()) {
+ homeservers.Add(await room.GetOriginHomeserverAsync());
+ }
+ }
+ }
+
var fullJoinUrl = $"{joinUrl}?server_name=" + string.Join("&server_name=", homeservers);
var res = await ClientHttpClient.PostAsJsonAsync(fullJoinUrl, new {
reason
@@ -397,14 +409,14 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
private Dictionary<string, string>? _namedFilterCache;
private Dictionary<string, SyncFilter> _filterCache = new();
- public async Task<JsonObject?> GetCapabilitiesAsync() {
+ public async Task<CapabilitiesResponse> GetCapabilitiesAsync() {
var res = await ClientHttpClient.GetAsync("/_matrix/client/v3/capabilities");
if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to get capabilities: {await res.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to get capabilities: {await res.Content.ReadAsStringAsync()}");
}
- return await res.Content.ReadFromJsonAsync<JsonObject>();
+ return await res.Content.ReadFromJsonAsync<CapabilitiesResponse>();
}
public class HsNamedCaches {
@@ -574,9 +586,45 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
await SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoredUserList);
}
- private class CapabilitiesResponse {
+ public class CapabilitiesResponse {
[JsonPropertyName("capabilities")]
- public Dictionary<string, object>? Capabilities { get; set; }
+ public CapabilitiesContents Capabilities { get; set; }
+
+ public class CapabilitiesContents {
+ [JsonPropertyName("m.3pid_changes")]
+ public BooleanCapability? ThreePidChanges { get; set; }
+
+ [JsonPropertyName("m.change_password")]
+ public BooleanCapability? ChangePassword { get; set; }
+
+ [JsonPropertyName("m.get_login_token")]
+ public BooleanCapability? GetLoginToken { get; set; }
+
+ [JsonPropertyName("m.room_versions")]
+ public RoomVersionsCapability? RoomVersions { get; set; }
+
+ [JsonPropertyName("m.set_avatar_url")]
+ public BooleanCapability? SetAvatarUrl { get; set; }
+
+ [JsonPropertyName("m.set_displayname")]
+ public BooleanCapability? SetDisplayName { get; set; }
+
+ [JsonExtensionData]
+ public Dictionary<string, object>? AdditionalCapabilities { get; set; }
+ }
+
+ public class BooleanCapability {
+ [JsonPropertyName("enabled")]
+ public bool Enabled { get; set; }
+ }
+
+ public class RoomVersionsCapability {
+ [JsonPropertyName("default")]
+ public string? Default { get; set; }
+
+ [JsonPropertyName("available")]
+ public Dictionary<string, string>? Available { get; set; }
+ }
}
#region Room Directory/aliases
diff --git a/LibMatrix/Homeservers/FederationClient.cs b/LibMatrix/Homeservers/FederationClient.cs
index a2cb12d..9760e20 100644
--- a/LibMatrix/Homeservers/FederationClient.cs
+++ b/LibMatrix/Homeservers/FederationClient.cs
@@ -1,7 +1,6 @@
-using System.Text.Json.Serialization;
using LibMatrix.Extensions;
+using LibMatrix.Responses.Federation;
using LibMatrix.Services;
-using Microsoft.VisualBasic.CompilerServices;
namespace LibMatrix.Homeservers;
@@ -18,81 +17,7 @@ public class FederationClient {
public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; }
public async Task<ServerVersionResponse> GetServerVersionAsync() => await HttpClient.GetFromJsonAsync<ServerVersionResponse>("/_matrix/federation/v1/version");
- public async Task<ServerKeysResponse> GetServerKeysAsync() => await HttpClient.GetFromJsonAsync<ServerKeysResponse>("/_matrix/key/v2/server");
+ public async Task<SignedObject<ServerKeysResponse>> GetServerKeysAsync() => await HttpClient.GetFromJsonAsync<SignedObject<ServerKeysResponse>>("/_matrix/key/v2/server");
}
-public class ServerKeysResponse {
- [JsonPropertyName("server_name")]
- public string ServerName { get; set; }
- [JsonPropertyName("valid_until_ts")]
- public ulong ValidUntilTs { get; set; }
-
- [JsonIgnore]
- public DateTime ValidUntil {
- get => DateTimeOffset.FromUnixTimeMilliseconds((long)ValidUntilTs).DateTime;
- set => ValidUntilTs = (ulong)new DateTimeOffset(value).ToUnixTimeMilliseconds();
- }
-
- [JsonPropertyName("verify_keys")]
- public Dictionary<string, CurrentVerifyKey> VerifyKeys { get; set; } = new();
-
- [JsonIgnore]
- public Dictionary<VersionedKeyId, CurrentVerifyKey> VerifyKeysById {
- get => VerifyKeys.ToDictionary(key => (VersionedKeyId)key.Key, key => key.Value);
- set => VerifyKeys = value.ToDictionary(key => (string)key.Key, key => key.Value);
- }
-
- [JsonPropertyName("old_verify_keys")]
- public Dictionary<string, ExpiredVerifyKey> OldVerifyKeys { get; set; } = new();
-
- [JsonIgnore]
- public Dictionary<VersionedKeyId, ExpiredVerifyKey> OldVerifyKeysById {
- get => OldVerifyKeys.ToDictionary(key => (VersionedKeyId)key.Key, key => key.Value);
- set => OldVerifyKeys = value.ToDictionary(key => (string)key.Key, key => key.Value);
- }
-
- public class VersionedKeyId {
- public required string Algorithm { get; set; }
- public required string KeyId { get; set; }
-
- public static implicit operator VersionedKeyId(string key) {
- var parts = key.Split(':', 2);
- if (parts.Length != 2) throw new ArgumentException("Invalid key format. Expected 'algorithm:keyId'.", nameof(key));
- return new VersionedKeyId { Algorithm = parts[0], KeyId = parts[1] };
- }
-
- public static implicit operator string(VersionedKeyId key) => $"{key.Algorithm}:{key.KeyId}";
- public static implicit operator (string, string)(VersionedKeyId key) => (key.Algorithm, key.KeyId);
- public static implicit operator VersionedKeyId((string algorithm, string keyId) key) => (key.algorithm, key.keyId);
- }
-
- public class CurrentVerifyKey {
- [JsonPropertyName("key")]
- public string Key { get; set; }
- }
-
- public class ExpiredVerifyKey : CurrentVerifyKey {
- [JsonPropertyName("expired_ts")]
- public ulong ExpiredTs { get; set; }
-
- [JsonIgnore]
- public DateTime Expired {
- get => DateTimeOffset.FromUnixTimeMilliseconds((long)ExpiredTs).DateTime;
- set => ExpiredTs = (ulong)new DateTimeOffset(value).ToUnixTimeMilliseconds();
- }
- }
-}
-
-public class ServerVersionResponse {
- [JsonPropertyName("server")]
- public required ServerInfo Server { get; set; }
-
- public class ServerInfo {
- [JsonPropertyName("name")]
- public string Name { get; set; }
-
- [JsonPropertyName("version")]
- public string Version { get; set; }
- }
-}
\ No newline at end of file
|