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.cs93
1 files changed, 71 insertions, 22 deletions
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs

index f9e3d04..f0b35f9 100644 --- a/LibMatrix/Homeservers/RemoteHomeServer.cs +++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -1,35 +1,36 @@ using System.Net.Http.Json; +using System.Text; using System.Text.Json; -using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Web; using ArcaneLibs.Extensions; using LibMatrix.Extensions; using LibMatrix.Responses; using LibMatrix.Services; -using Microsoft.Extensions.Logging.Abstractions; namespace LibMatrix.Homeservers; public class RemoteHomeserver { - public RemoteHomeserver(string baseUrl, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { + public RemoteHomeserver(string serverName, HomeserverResolverService.WellKnownUris wellKnownUris, string? proxy) { if (string.IsNullOrWhiteSpace(proxy)) proxy = null; - BaseUrl = baseUrl; + ServerNameOrUrl = serverName; WellKnownUris = wellKnownUris; + Proxy = proxy; ClientHttpClient = new MatrixHttpClient { - BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {baseUrl}!")), + BaseAddress = new Uri(proxy?.TrimEnd('/') ?? wellKnownUris.Client?.TrimEnd('/') ?? throw new InvalidOperationException($"No client URI for {serverName}!")), // Timeout = TimeSpan.FromSeconds(300) // TODO: Re-implement this }; - if (proxy is not null) ClientHttpClient.DefaultRequestHeaders.Add("MXAE_UPSTREAM", baseUrl); + 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(); - public string BaseUrl { get; } + public string ServerNameOrUrl { get; } + public string? Proxy { get; } [JsonIgnore] public MatrixHttpClient ClientHttpClient { get; set; } @@ -51,16 +52,18 @@ public class RemoteHomeserver { 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; + _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<ClientVersionsResponse> GetClientVersionsAsync() { var resp = await ClientHttpClient.GetAsync($"/_matrix/client/versions"); var data = await resp.Content.ReadFromJsonAsync<ClientVersionsResponse>(); if (!resp.IsSuccessStatusCode) Console.WriteLine("ClientVersions: " + data); - return data; + return data ?? throw new InvalidOperationException("ClientVersionsResponse is null"); } public async Task<AliasResult> ResolveRoomAliasAsync(string alias) { @@ -68,7 +71,16 @@ public class RemoteHomeserver { var data = await resp.Content.ReadFromJsonAsync<AliasResult>(); //var text = await resp.Content.ReadAsStringAsync(); if (!resp.IsSuccessStatusCode) Console.WriteLine("ResolveAlias: " + data.ToJson()); - return data; + 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))); + + // 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)); } #region Authentication @@ -80,12 +92,11 @@ public class RemoteHomeserver { type = "m.id.user", user = username }, - password = password, + password, initial_device_display_name = deviceName }); var data = await resp.Content.ReadFromJsonAsync<LoginResponse>(); - if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data.ToJson()); - return data; + return data ?? throw new InvalidOperationException("LoginResponse is null"); } public async Task<LoginResponse> RegisterAsync(string username, string password, string? deviceName = null) { @@ -101,26 +112,64 @@ public class RemoteHomeserver { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); var data = await resp.Content.ReadFromJsonAsync<LoginResponse>(); - if (!resp.IsSuccessStatusCode) Console.WriteLine("Register: " + data.ToJson()); - return data; + return data ?? throw new InvalidOperationException("LoginResponse is null"); } #endregion [Obsolete("This call uses the deprecated unauthenticated media endpoints, please switch to the relevant AuthenticatedHomeserver methods instead.", true)] - public string? ResolveMediaUri(string? mxcUri) { - if (mxcUri is null) return null; - if (mxcUri.StartsWith("https://")) return mxcUri; - return $"{ClientHttpClient.BaseAddress}/_matrix/media/v3/download/{mxcUri.Replace("mxc://", "")}".Replace("//_matrix", "/_matrix"); - } + public virtual string? ResolveMediaUri(string? mxcUri) => null; public UserInteractiveAuthClient Auth; } +public class PublicRoomDirectoryResult { + [JsonPropertyName("chunk")] + public List<PublicRoomListItem> Chunk { get; set; } + + [JsonPropertyName("next_batch")] + public string? NextBatch { get; set; } + + [JsonPropertyName("prev_batch")] + public string? PrevBatch { get; set; } + + [JsonPropertyName("total_room_count_estimate")] + public int TotalRoomCountEstimate { get; set; } + + public class PublicRoomListItem { + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } + + [JsonPropertyName("canonical_alias")] + public string? CanonicalAlias { get; set; } + + [JsonPropertyName("guest_can_join")] + public bool GuestCanJoin { get; set; } + + [JsonPropertyName("join_rule")] + public string JoinRule { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("num_joined_members")] + public int NumJoinedMembers { get; set; } + + [JsonPropertyName("room_id")] + public string RoomId { get; set; } + + [JsonPropertyName("topic")] + public string? Topic { get; set; } + + [JsonPropertyName("world_readable")] + public bool WorldReadable { get; set; } + } +} + public class AliasResult { [JsonPropertyName("room_id")] - public string RoomId { get; set; } = null!; + public string RoomId { get; set; } [JsonPropertyName("servers")] - public List<string> Servers { get; set; } = null!; + public List<string> Servers { get; set; } } \ No newline at end of file