about summary refs log tree commit diff
path: root/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
diff options
context:
space:
mode:
Diffstat (limited to 'LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs')
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs124
1 files changed, 108 insertions, 16 deletions
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs

index 55899de..916780e 100644 --- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs +++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Web; +using ArcaneLibs.Collections; using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec; using LibMatrix.EventTypes.Spec.State.RoomInfo; @@ -13,6 +14,7 @@ using LibMatrix.Homeservers.Extensions.NamedCaches; using LibMatrix.Responses; using LibMatrix.RoomTypes; using LibMatrix.Services; +using LibMatrix.StructuredData; using LibMatrix.Utilities; namespace LibMatrix.Homeservers; @@ -145,7 +147,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { await Task.Delay(1000); } } - }).ToAsyncEnumerable(); + }).ToAsyncResultEnumerable(); await foreach (var result in tasks) if (result is not null) @@ -215,7 +217,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { if (preserveCustomRoomProfile) { var rooms = await GetJoinedRooms(); - var roomProfiles = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncEnumerable(); + var roomProfiles = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncResultEnumerable(); targetSyncCount = rooms.Count; await foreach (var (roomId, currentRoomProfile) in roomProfiles) try { @@ -288,14 +290,26 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { public async IAsyncEnumerable<KeyValuePair<string, RoomMemberEventContent>> GetRoomProfilesAsync() { var rooms = await GetJoinedRooms(); - var results = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncEnumerable(); + var results = rooms.Select(GetOwnRoomProfileWithIdAsync).ToAsyncResultEnumerable(); await foreach (var res in results) yield return res; } 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,15 +411,12 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { private Dictionary<string, string>? _namedFilterCache; private Dictionary<string, SyncFilter> _filterCache = new(); - public async Task<JsonObject?> 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()}"); - } + private static readonly SemaphoreCache<CapabilitiesResponse> CapabilitiesCache = new(); - return await res.Content.ReadFromJsonAsync<JsonObject>(); - } + public async Task<CapabilitiesResponse> GetCapabilitiesAsync() => + await CapabilitiesCache.GetOrAdd(ServerName, async () => + await ClientHttpClient.GetFromJsonAsync<CapabilitiesResponse>("/_matrix/client/v3/capabilities") + ); public class HsNamedCaches { internal HsNamedCaches(AuthenticatedHomeserverGeneric hs) { @@ -429,7 +440,8 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { try { // Console.WriteLine($"Trying authenticated media URL: {uri}"); var res = await ClientHttpClient.SendAsync(new() { - Method = HttpMethod.Head, + // Method = HttpMethod.Head, // This apparently doesn't work with Matrix-Media-Repo... + Method = HttpMethod.Get, RequestUri = (new Uri(mxcUri.ToDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute)) }); if (res.IsSuccessStatusCode) { @@ -445,7 +457,8 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver { try { // Console.WriteLine($"Trying legacy media URL: {uri}"); var res = await ClientHttpClient.SendAsync(new() { - Method = HttpMethod.Head, + // Method = HttpMethod.Head, + Method = HttpMethod.Get, RequestUri = new(mxcUri.ToLegacyDownloadUri(BaseUrl, filename, timeout), string.IsNullOrWhiteSpace(BaseUrl) ? UriKind.Relative : UriKind.Absolute) }); if (res.IsSuccessStatusCode) { @@ -574,8 +587,87 @@ 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; } + + [JsonPropertyName("gay.rory.bulk_send_events")] + public BooleanCapability? BulkSendEvents { get; set; } + + [JsonPropertyName("gay.rory.synapse_admin_extensions.room_list.query_events.v2")] + public BooleanCapability? SynapseRoomListQueryEventsV2 { 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 + + public async Task SetRoomAliasAsync(string roomAlias, string roomId) { + var resp = await ClientHttpClient.PutAsJsonAsync($"/_matrix/client/v3/directory/room/{HttpUtility.UrlEncode(roomAlias)}", new RoomIdResponse() { + RoomId = roomId + }); + if (!resp.IsSuccessStatusCode) { + Console.WriteLine($"Failed to set room alias: {await resp.Content.ReadAsStringAsync()}"); + throw new InvalidDataException($"Failed to set room alias: {await resp.Content.ReadAsStringAsync()}"); + } } + + public async Task DeleteRoomAliasAsync(string roomAlias) { + var resp = await ClientHttpClient.DeleteAsync("/_matrix/client/v3/directory/room/" + HttpUtility.UrlEncode(roomAlias)); + if (!resp.IsSuccessStatusCode) { + Console.WriteLine($"Failed to set room alias: {await resp.Content.ReadAsStringAsync()}"); + throw new InvalidDataException($"Failed to set room alias: {await resp.Content.ReadAsStringAsync()}"); + } + } + + public async Task<RoomAliasesResponse> GetLocalRoomAliasesAsync(string roomId) { + var resp = await ClientHttpClient.GetAsync($"/_matrix/client/v3/rooms/{HttpUtility.UrlEncode(roomId)}/aliases"); + if (!resp.IsSuccessStatusCode) { + Console.WriteLine($"Failed to get room aliases: {await resp.Content.ReadAsStringAsync()}"); + throw new InvalidDataException($"Failed to get room aliases: {await resp.Content.ReadAsStringAsync()}"); + } + + return await resp.Content.ReadFromJsonAsync<RoomAliasesResponse>() ?? throw new Exception("Failed to get room aliases?"); + } + + public class RoomAliasesResponse { + [JsonPropertyName("aliases")] + public required List<string> Aliases { get; set; } + } + +#endregion } \ No newline at end of file