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

index f0b35f9..54f5937 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -3,6 +3,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Web; +using ArcaneLibs.Collections; using ArcaneLibs.Extensions; using LibMatrix.Extensions; using LibMatrix.Responses; @@ -22,13 +23,13 @@ public class RemoteHomeserver { // Timeout = TimeSpan.FromSeconds(300) // TODO: Re-implement this }; - if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", serverName); if (!string.IsNullOrWhiteSpace(wellKnownUris.Server)) FederationClient = new FederationClient(WellKnownUris.Server!, proxy); Auth = new(this); } - private Dictionary<string, object> _profileCache { get; set; } = new(); + // private Dictionary<string, object> _profileCache { get; set; } = new(); + private SemaphoreCache<UserProfileResponse> _profileCache { get; set; } = new(); public string ServerNameOrUrl { get; } public string? Proxy { get; } @@ -40,27 +41,12 @@ public class RemoteHomeserver { public HomeserverResolverService.WellKnownUris WellKnownUris { get; set; } - public async Task<UserProfileResponse> GetProfileAsync(string mxid, bool useCache = false) { - if (mxid is null) throw new ArgumentNullException(nameof(mxid)); - if (useCache && _profileCache.TryGetValue(mxid, out var value)) { - if (value is SemaphoreSlim s) await s.WaitAsync(); - if (value is UserProfileResponse p) return p; - } - - _profileCache[mxid] = new SemaphoreSlim(1); - - var resp = await ClientHttpClient.GetAsync($"/_matrix/client/v3/profile/{HttpUtility.UrlEncode(mxid)}"); - var data = await resp.Content.ReadFromJsonAsync<UserProfileResponse>(); - if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data); - _profileCache[mxid] = data ?? throw new InvalidOperationException($"Could not get profile for {mxid}"); - - return data; - } - // TODO: Do we need to support retrieving individual profile properties? Is there any use for that besides just getting the full profile? + public async Task<UserProfileResponse> GetProfileAsync(string mxid) => + await ClientHttpClient.GetFromJsonAsync<UserProfileResponse>($"/_matrix/client/v3/profile/{HttpUtility.UrlEncode(mxid)}"); public async Task<ClientVersionsResponse> GetClientVersionsAsync() { - var resp = await ClientHttpClient.GetAsync($"/_matrix/client/versions"); + var resp = await ClientHttpClient.GetAsync("/_matrix/client/versions"); var data = await resp.Content.ReadFromJsonAsync<ClientVersionsResponse>(); if (!resp.IsSuccessStatusCode) Console.WriteLine("ClientVersions: " + data); return data ?? throw new InvalidOperationException("ClientVersionsResponse is null"); @@ -74,15 +60,54 @@ public class RemoteHomeserver { return data ?? throw new InvalidOperationException($"Could not resolve alias {alias}"); } - public Task<PublicRoomDirectoryResult> GetPublicRoomsAsync(int limit = 100, string? server = null, string? since = null) => - ClientHttpClient.GetFromJsonAsync<PublicRoomDirectoryResult>(buildUriWithParams("/_matrix/client/v3/publicRooms", (nameof(limit), true, limit), - (nameof(server), !string.IsNullOrWhiteSpace(server), server), (nameof(since), !string.IsNullOrWhiteSpace(since), since))); + public async Task<PublicRoomDirectoryResult> GetPublicRoomsAsync(int limit = 100, string? server = null, string? since = null, string? thirdPartyInstanceId = null, + bool? includeAllNetworks = null, RoomDirectoryFilter? filter = null) { + if (thirdPartyInstanceId is null && includeAllNetworks is null && filter is null) { + var url = $"/_matrix/client/v3/publicRooms?limit={limit}"; + if (!string.IsNullOrWhiteSpace(server)) { + url += $"&server={server}"; + } + + if (!string.IsNullOrWhiteSpace(since)) { + url += $"&since={since}"; + } + + return await ClientHttpClient.GetFromJsonAsync<PublicRoomDirectoryResult>(url); + } - // TODO: move this somewhere else - private string buildUriWithParams(string url, params (string name, bool include, object? value)[] values) { - return url + "?" + string.Join("&", values.Where(x => x.include)); + // this technically requires authentication... TODO: move to AuthenticatedHomeserver? + var postUrl = "/_matrix/client/v3/publicRooms"; + if (!string.IsNullOrWhiteSpace(server)) { + postUrl += $"?server={HttpUtility.UrlEncode(server)}"; + } + + var postData = new RoomDirectoryFilteredRequest { + Limit = limit, + Since = since, + ThirdPartyInstanceId = thirdPartyInstanceId, + IncludeAllNetworks = includeAllNetworks, + Filter = filter + }; + + return await (await ClientHttpClient.PostAsJsonAsync(postUrl, postData)).EnsureSuccessStatusCode() + .Content.ReadFromJsonAsync<PublicRoomDirectoryResult>() ?? throw new InvalidOperationException(); } + public async IAsyncEnumerable<PublicRoomDirectoryResult> EnumeratePublicRoomsAsync(int limit = int.MaxValue, string? server = null, string? since = null, + string? thirdPartyInstanceId = null, bool? includeAllNetworks = null, RoomDirectoryFilter? filter = null, int chunkSize = 100) { + PublicRoomDirectoryResult res; + do { + res = await GetPublicRoomsAsync(chunkSize, server, since, thirdPartyInstanceId, includeAllNetworks, filter); + yield return res; + if (res.NextBatch is null || res.NextBatch == since || res.Chunk.Count == 0) break; + since = res.NextBatch; + } while (limit > 0 && limit-- > 0); + } + + public async Task<RoomDirectoryVisibilityResponse> GetRoomDirectoryVisibilityAsync(string roomId) + => await (await ClientHttpClient.GetAsync($"/_matrix/client/v3/directory/list/room/{HttpUtility.UrlEncode(roomId)}")).Content + .ReadFromJsonAsync<RoomDirectoryVisibilityResponse>() ?? throw new InvalidOperationException(); + #region Authentication public async Task<LoginResponse> LoginAsync(string username, string password, string? deviceName = null) { @@ -117,12 +142,45 @@ public class RemoteHomeserver { #endregion - [Obsolete("This call uses the deprecated unauthenticated media endpoints, please switch to the relevant AuthenticatedHomeserver methods instead.", true)] - public virtual string? ResolveMediaUri(string? mxcUri) => null; - public UserInteractiveAuthClient Auth; } +public class RoomDirectoryFilteredRequest { + [JsonPropertyName("filter")] + public RoomDirectoryFilter? Filter { get; set; } + + [JsonPropertyName("include_all_networks")] + public bool? IncludeAllNetworks { get; set; } + + [JsonPropertyName("limit")] + public int Limit { get; set; } + + [JsonPropertyName("since")] + public string? Since { get; set; } + + [JsonPropertyName("third_party_instance_id")] + public string? ThirdPartyInstanceId { get; set; } +} + +public class RoomDirectoryFilter { + [JsonPropertyName("generic_search_term")] + public string? GenericSearchTerm { get; set; } + + [JsonPropertyName("room_types")] + public List<string?>? RoomTypes { get; set; } +} + +public class RoomDirectoryVisibilityResponse { + [JsonPropertyName("visibility")] + public VisibilityValue Visibility { get; set; } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum VisibilityValue { + [JsonStringEnumMemberName("public")] Public, + [JsonStringEnumMemberName("private")] Private + } +} + public class PublicRoomDirectoryResult { [JsonPropertyName("chunk")] public List<PublicRoomListItem> Chunk { get; set; }