diff --git a/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs b/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs
index b76b176..f8ee58b 100644
--- a/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs
+++ b/LibMatrix/EventTypes/Spec/RoomMessageEventData.cs
@@ -28,4 +28,6 @@ public class RoomMessageEventContent : EventContent {
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }
+
+ public string? FileName { get; set; }
}
diff --git a/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs
index e409f3a..c5bf14e 100644
--- a/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs
+++ b/LibMatrix/EventTypes/Spec/State/RoomCreateEventData.cs
@@ -8,12 +8,16 @@ namespace LibMatrix.EventTypes.Spec.State;
public class RoomCreateEventContent : EventContent {
[JsonPropertyName("room_version")]
public string? RoomVersion { get; set; }
+
[JsonPropertyName("creator")]
public string? Creator { get; set; }
+
[JsonPropertyName("m.federate")]
public bool? Federate { get; set; }
+
[JsonPropertyName("predecessor")]
public RoomCreatePredecessor? Predecessor { get; set; }
+
[JsonPropertyName("type")]
public string? Type { get; set; }
diff --git a/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs b/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs
index 1a5d5f5..2ae9593 100644
--- a/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs
+++ b/LibMatrix/EventTypes/Spec/State/RoomPowerLevelEventData.cs
@@ -7,34 +7,34 @@ namespace LibMatrix.EventTypes.Spec.State;
[MatrixEvent(EventName = "m.room.power_levels")]
public class RoomPowerLevelEventContent : EventContent {
[JsonPropertyName("ban")]
- public long? Ban { get; set; } // = 50;
+ public long? Ban { get; set; } = 50;
[JsonPropertyName("events_default")]
- public long EventsDefault { get; set; } // = 0;
+ public long EventsDefault { get; set; } = 0;
[JsonPropertyName("events")]
public Dictionary<string, long>? Events { get; set; } // = null!;
[JsonPropertyName("invite")]
- public long? Invite { get; set; } // = 50;
+ public long? Invite { get; set; } = 0;
[JsonPropertyName("kick")]
- public long? Kick { get; set; } // = 50;
+ public long? Kick { get; set; } = 50;
[JsonPropertyName("notifications")]
public NotificationsPL? NotificationsPl { get; set; } // = null!;
[JsonPropertyName("redact")]
- public long? Redact { get; set; } // = 50;
+ public long? Redact { get; set; } = 50;
[JsonPropertyName("state_default")]
- public long? StateDefault { get; set; } // = 50;
+ public long? StateDefault { get; set; } = 50;
[JsonPropertyName("users")]
public Dictionary<string, long>? Users { get; set; } // = null!;
[JsonPropertyName("users_default")]
- public long? UsersDefault { get; set; } // = 0;
+ public long? UsersDefault { get; set; } = 0;
[Obsolete("Historical was a key related to MSC2716, a spec change on backfill that was dropped!", true)]
[JsonIgnore]
@@ -47,7 +47,7 @@ public class RoomPowerLevelEventContent : EventContent {
}
public bool IsUserAdmin(string userId) {
- return Users.TryGetValue(userId, out var level) && level >= Events.Max(x=>x.Value);
+ return Users.TryGetValue(userId, out var level) && level >= Events.Max(x => x.Value);
}
public bool UserHasPermission(string userId, string eventType) {
diff --git a/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs b/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs
index a13ba2e..0a897dc 100644
--- a/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs
+++ b/LibMatrix/EventTypes/Spec/State/SpaceChildEventData.cs
@@ -9,7 +9,7 @@ public class SpaceChildEventContent : EventContent {
[JsonPropertyName("auto_join")]
public bool? AutoJoin { get; set; }
[JsonPropertyName("via")]
- public string[]? Via { get; set; }
+ public List<string>? Via { get; set; }
[JsonPropertyName("suggested")]
public bool? Suggested { get; set; }
}
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index 386fd4d..74972a1 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -16,10 +16,6 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver) {
string? setPresence = "online",
SyncFilter? filter = null,
CancellationToken? cancellationToken = null) {
- // var outFileName = "sync-" +
- // (await storageService.CacheStorageProvider.GetAllKeysAsync()).Count(
- // x => x.StartsWith("sync")) +
- // ".json";
var url = $"/_matrix/client/v3/sync?timeout={timeout}&set_presence={setPresence}";
if (!string.IsNullOrWhiteSpace(since)) url += $"&since={since}";
if (filter is not null) url += $"&filter={filter.ToJson(ignoreNull: true, indent: false)}";
@@ -28,19 +24,17 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver) {
try {
var req = await homeserver._httpClient.GetAsync(url, cancellationToken: cancellationToken ?? CancellationToken.None);
- // var res = await JsonSerializer.DeserializeAsync<SyncResult>(await req.Content.ReadAsStreamAsync());
-
#if DEBUG && false
- var jsonObj = await req.Content.ReadFromJsonAsync<JsonElement>();
try {
- await _homeServer._httpClient.PostAsJsonAsync(
- "http://localhost:5116/validate/" + typeof(SyncResult).AssemblyQualifiedName, jsonObj);
+ await homeserver._httpClient.PostAsync(
+ "http://localhost:5116/validate/" + typeof(SyncResult).AssemblyQualifiedName,
+ new StreamContent(await req.Content.ReadAsStreamAsync()));
}
+
catch (Exception e) {
Console.WriteLine("[!!] Checking sync response failed: " + e);
}
-
- var res = jsonObj.Deserialize<SyncResult>();
+ var res = await req.Content.ReadFromJsonAsync<SyncResult>();
return res;
#else
return await req.Content.ReadFromJsonAsync<SyncResult>();
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index b881e6c..f70dd39 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -1,3 +1,4 @@
+using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -12,18 +13,30 @@ using LibMatrix.Services;
namespace LibMatrix.Homeservers;
public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
- public AuthenticatedHomeserverGeneric(string canonicalHomeServerDomain, string accessToken) : base(canonicalHomeServerDomain) {
+ public AuthenticatedHomeserverGeneric(string baseUrl, string accessToken) : base(baseUrl) {
AccessToken = accessToken.Trim();
SyncHelper = new SyncHelper(this);
+
+ _httpClient.Timeout = TimeSpan.FromMinutes(15);
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
public virtual SyncHelper SyncHelper { get; init; }
- public virtual WhoAmIResponse WhoAmI { get; set; } = null!;
- public virtual string UserId => WhoAmI.UserId;
+ private WhoAmIResponse? _whoAmI;
+
+ public WhoAmIResponse? WhoAmI => _whoAmI ??= _httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami").Result;
+ public string UserId => WhoAmI.UserId;
+
+ // 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 virtual string AccessToken { get; set; }
public virtual GenericRoom GetRoom(string roomId) {
- if(roomId is null || !roomId.StartsWith("!")) throw new ArgumentException("Room ID must start with !", nameof(roomId));
+ if (roomId is null || !roomId.StartsWith("!")) throw new ArgumentException("Room ID must start with !", nameof(roomId));
return new GenericRoom(this, roomId);
}
@@ -50,7 +63,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
}
public virtual async Task<GenericRoom> CreateRoom(CreateRoomRequest creationEvent) {
- creationEvent.CreationContent["creator"] = UserId;
+ creationEvent.CreationContent["creator"] = WhoAmI.UserId;
var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent, new JsonSerializerOptions {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
});
@@ -61,9 +74,10 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
var room = GetRoom((await res.Content.ReadFromJsonAsync<JsonObject>())!["room_id"]!.ToString());
- foreach (var user in creationEvent.Invite) {
- await room.InviteUser(user);
- }
+ if (creationEvent.Invite is not null)
+ foreach (var user in creationEvent.Invite) {
+ await room.InviteUserAsync(user);
+ }
return room;
}
@@ -77,6 +91,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
}
#region Utility Functions
+
public virtual async IAsyncEnumerable<GenericRoom> GetJoinedRoomsByType(string type) {
var rooms = await GetJoinedRooms();
var tasks = rooms.Select(async room => {
@@ -92,6 +107,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
if (result is not null) yield return result;
}
}
+
#endregion
#region Account Data
@@ -104,11 +120,11 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
// }
//
// return await res.Content.ReadFromJsonAsync<T>();
- return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/user/{UserId}/account_data/{key}");
+ return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}");
}
public virtual async Task SetAccountData(string key, object data) {
- var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{UserId}/account_data/{key}", data);
+ var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}", data);
if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to set account data: {await res.Content.ReadAsStringAsync()}");
throw new InvalidDataException($"Failed to set account data: {await res.Content.ReadAsStringAsync()}");
@@ -116,4 +132,6 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeServer {
}
#endregion
+
+
}
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs
index 5319f46..a420d71 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverMxApiExtended.cs
@@ -4,5 +4,4 @@ using LibMatrix.Services;
namespace LibMatrix.Homeservers;
-public class AuthenticatedHomeserverMxApiExtended(string canonicalHomeServerDomain, string accessToken) : AuthenticatedHomeserverGeneric(canonicalHomeServerDomain,
- accessToken);
+public class AuthenticatedHomeserverMxApiExtended(string baseUrl, string accessToken) : AuthenticatedHomeserverGeneric(baseUrl, accessToken);
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
index ae26f69..e355d2d 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
@@ -102,7 +102,7 @@ public class AuthenticatedHomeserverSynapse : AuthenticatedHomeserverGeneric {
}
}
- public AuthenticatedHomeserverSynapse(string canonicalHomeServerDomain, string accessToken) : base(canonicalHomeServerDomain, accessToken) {
+ public AuthenticatedHomeserverSynapse(string baseUrl, string accessToken) : base(baseUrl, accessToken) {
Admin = new(this);
}
}
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index ab3ab51..d10c837 100644
--- a/LibMatrix/Homeservers/RemoteHomeServer.cs
+++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -1,19 +1,22 @@
using System.Net.Http.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
using ArcaneLibs.Extensions;
using LibMatrix.EventTypes.Spec.State;
using LibMatrix.Extensions;
using LibMatrix.Responses;
+using LibMatrix.Services;
namespace LibMatrix.Homeservers;
-public class RemoteHomeServer(string canonicalHomeServerDomain) {
- // _httpClient.Timeout = TimeSpan.FromSeconds(5);
+public class RemoteHomeServer(string baseUrl) {
private Dictionary<string, object> _profileCache { get; set; } = new();
- public string HomeServerDomain { get; } = canonicalHomeServerDomain.Trim();
- public string FullHomeServerDomain { get; set; }
- public MatrixHttpClient _httpClient { get; set; } = new();
+ public string BaseUrl { get; } = baseUrl.Trim();
+ public MatrixHttpClient _httpClient { get; set; } = new() {
+ BaseAddress = new Uri(new HomeserverResolverService().ResolveHomeserverFromWellKnown(baseUrl).Result ?? throw new InvalidOperationException("Failed to resolve homeserver")),
+ Timeout = TimeSpan.FromSeconds(120)
+ };
public async Task<ProfileResponseEventContent> GetProfileAsync(string mxid) {
if (mxid is null) throw new ArgumentNullException(nameof(mxid));
@@ -46,6 +49,42 @@ public class RemoteHomeServer(string canonicalHomeServerDomain) {
if (!resp.IsSuccessStatusCode) Console.WriteLine("ResolveAlias: " + data.ToJson());
return data;
}
+
+#region Authentication
+
+ public async Task<LoginResponse> LoginAsync(string username, string password, string? deviceName = null) {
+ var resp = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/login", new {
+ type = "m.login.password",
+ identifier = new {
+ type = "m.id.user",
+ user = username
+ },
+ password = password,
+ initial_device_display_name = deviceName
+ });
+ var data = await resp.Content.ReadFromJsonAsync<LoginResponse>();
+ if (!resp.IsSuccessStatusCode) Console.WriteLine("Login: " + data.ToJson());
+ return data;
+ }
+
+ public async Task<LoginResponse> RegisterAsync(string username, string password, string? deviceName = null) {
+ var resp = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/register", new {
+ kind = "user",
+ auth = new {
+ type = "m.login.dummy"
+ },
+ username,
+ password,
+ initial_device_display_name = deviceName ?? "LibMatrix"
+ }, new JsonSerializerOptions() {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+ });
+ var data = await resp.Content.ReadFromJsonAsync<LoginResponse>();
+ if (!resp.IsSuccessStatusCode) Console.WriteLine("Register: " + data.ToJson());
+ return data;
+ }
+
+#endregion
}
public class AliasResult {
diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs
index 381271b..511b3da 100644
--- a/LibMatrix/Responses/CreateRoomRequest.cs
+++ b/LibMatrix/Responses/CreateRoomRequest.cs
@@ -40,7 +40,7 @@ public class CreateRoomRequest {
public JsonObject CreationContent { get; set; } = new();
[JsonPropertyName("invite")]
- public List<string> Invite { get; set; }
+ public List<string>? Invite { get; set; }
/// <summary>
/// For use only when you can't use the CreationContent property
diff --git a/LibMatrix/Responses/LoginResponse.cs b/LibMatrix/Responses/LoginResponse.cs
index 175f337..eb53c0a 100644
--- a/LibMatrix/Responses/LoginResponse.cs
+++ b/LibMatrix/Responses/LoginResponse.cs
@@ -1,19 +1,30 @@
using System.Text.Json.Serialization;
+using LibMatrix.Homeservers;
+using LibMatrix.Services;
namespace LibMatrix.Responses;
public class LoginResponse {
[JsonPropertyName("access_token")]
- public string AccessToken { get; set; }
+ public string AccessToken { get; set; } = null!;
[JsonPropertyName("device_id")]
- public string DeviceId { get; set; }
+ public string DeviceId { get; set; } = null!;
+
+ private string? _homeserver;
[JsonPropertyName("home_server")]
- public string Homeserver { get; set; }
+ public string Homeserver {
+ get => _homeserver ?? UserId.Split(':', 2).Last();
+ protected init => _homeserver = value;
+ }
[JsonPropertyName("user_id")]
- public string UserId { get; set; }
+ public string UserId { get; set; } = null!;
+
+ public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedHomeserver(string? proxy = null) {
+ return new AuthenticatedHomeserverGeneric(proxy ?? await new HomeserverResolverService().ResolveHomeserverFromWellKnown(Homeserver), AccessToken);
+ }
}
public class LoginRequest {
[JsonPropertyName("type")]
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index ab748fe..78a0873 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -28,14 +28,6 @@ public class GenericRoom {
public string RoomId { get; set; }
- [Obsolete("", true)]
- public async Task<JsonElement?> GetStateAsync(string type, string stateKey = "") {
- var url = $"/_matrix/client/v3/rooms/{RoomId}/state";
- if (!string.IsNullOrEmpty(type)) url += $"/{type}";
- if (!string.IsNullOrEmpty(stateKey)) url += $"/{stateKey}";
- return await _httpClient.GetFromJsonAsync<JsonElement>(url);
- }
-
public async IAsyncEnumerable<StateEventResponse?> GetFullStateAsync() {
var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/state");
var result =
@@ -68,7 +60,7 @@ public class GenericRoom {
}
catch (MatrixException e) {
// if (e is not { ErrorCodode: "M_NOT_FOUND" }) {
- throw;
+ throw;
// }
// Console.WriteLine(e);
@@ -202,21 +194,18 @@ public class GenericRoom {
return resu;
}
- public async Task<EventIdResponse> SendFileAsync(string eventType, string fileName, Stream fileStream) {
- var content = new MultipartFormDataContent();
- content.Add(new StreamContent(fileStream), "file", fileName);
- var res = await
- (
- await _httpClient.PutAsync(
- $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(),
- content
- )
- )
- .Content.ReadFromJsonAsync<EventIdResponse>();
- return res;
+ public async Task<EventIdResponse> SendFileAsync(string fileName, Stream fileStream, string messageType = "m.file") {
+ var url = await Homeserver.UploadFile(fileName, fileStream);
+ var content = new RoomMessageEventContent() {
+ MessageType = messageType,
+ Url = url,
+ Body = fileName,
+ FileName = fileName,
+ };
+ return await SendTimelineEventAsync("m.room.message", content);
}
- public async Task<T> GetRoomAccountData<T>(string key) {
+ public async Task<T> GetRoomAccountDataAsync<T>(string key) {
var res = await _httpClient.GetAsync($"/_matrix/client/v3/user/{Homeserver.UserId}/rooms/{RoomId}/account_data/{key}");
if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to get room account data: {await res.Content.ReadAsStringAsync()}");
@@ -226,7 +215,7 @@ public class GenericRoom {
return await res.Content.ReadFromJsonAsync<T>();
}
- public async Task SetRoomAccountData(string key, object data) {
+ public async Task SetRoomAccountDataAsync(string key, object data) {
var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{Homeserver.UserId}/rooms/{RoomId}/account_data/{key}", data);
if (!res.IsSuccessStatusCode) {
Console.WriteLine($"Failed to set room account data: {await res.Content.ReadAsStringAsync()}");
@@ -236,7 +225,7 @@ public class GenericRoom {
public readonly SpaceRoom AsSpace;
- public async Task<T> GetEvent<T>(string eventId) {
+ public async Task<T> GetEventAsync<T>(string eventId) {
return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}");
}
@@ -246,9 +235,38 @@ public class GenericRoom {
$"/_matrix/client/v3/rooms/{RoomId}/redact/{eventToRedact}/{Guid.NewGuid()}", data)).Content.ReadFromJsonAsync<EventIdResponse>())!;
}
- public async Task InviteUser(string userId, string? reason = null) {
+ public async Task InviteUserAsync(string userId, string? reason = null) {
await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/invite", new UserIdAndReason(userId, reason));
}
+
+#region Disband room
+
+ public async Task DisbandRoomAsync() {
+ var states = GetFullStateAsync();
+ List<string> stateTypeIgnore = new() {
+ "m.room.create",
+ "m.room.power_levels",
+ "m.room.join_rules",
+ "m.room.history_visibility",
+ "m.room.guest_access",
+ "m.room.member",
+ };
+ await foreach (var state in states) {
+ if (state is null || state.RawContent is not { Count: > 0 }) continue;
+ if (state.Type == "m.room.member" && state.StateKey != Homeserver.UserId)
+ try {
+ await BanAsync(state.StateKey, "Disbanding room");
+ }
+ catch (MatrixException e) {
+ if (e.ErrorCode != "M_FORBIDDEN") throw;
+ }
+
+ if (stateTypeIgnore.Contains(state.Type)) continue;
+ await SendStateEventAsync(state.Type, state.StateKey, new());
+ }
+ }
+
+#endregion
}
public class RoomIdResponse {
diff --git a/LibMatrix/RoomTypes/SpaceRoom.cs b/LibMatrix/RoomTypes/SpaceRoom.cs
index a43ae82..4a8e247 100644
--- a/LibMatrix/RoomTypes/SpaceRoom.cs
+++ b/LibMatrix/RoomTypes/SpaceRoom.cs
@@ -1,26 +1,37 @@
using ArcaneLibs.Extensions;
using LibMatrix.Homeservers;
+using Microsoft.Extensions.Logging;
namespace LibMatrix.RoomTypes;
-public class SpaceRoom : GenericRoom {
- private new readonly AuthenticatedHomeserverGeneric _homeserver;
+public class SpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomId) : GenericRoom(homeserver, roomId) {
private readonly GenericRoom _room;
- public SpaceRoom(AuthenticatedHomeserverGeneric homeserver, string roomId) : base(homeserver, roomId) {
- _homeserver = homeserver;
- }
-
- private static SemaphoreSlim _semaphore = new(1, 1);
public async IAsyncEnumerable<GenericRoom> GetChildrenAsync(bool includeRemoved = false) {
- // await _semaphore.WaitAsync();
var rooms = new List<GenericRoom>();
var state = GetFullStateAsync();
await foreach (var stateEvent in state) {
- if (stateEvent.Type != "m.space.child") continue;
- if (stateEvent.RawContent.ToJson() != "{}" || includeRemoved)
- yield return _homeserver.GetRoom(stateEvent.StateKey);
+ if (stateEvent!.Type != "m.space.child") continue;
+ if (stateEvent.RawContent!.ToJson() != "{}" || includeRemoved)
+ yield return Homeserver.GetRoom(stateEvent.StateKey);
+ }
+ }
+
+ public async Task<EventIdResponse> AddChildAsync(GenericRoom room) {
+ var members = room.GetMembersAsync(true);
+ Dictionary<string, int> memberCountByHs = new();
+ await foreach (var member in members) {
+ var server = member.StateKey.Split(':')[1];
+ if (memberCountByHs.ContainsKey(server)) memberCountByHs[server]++;
+ else memberCountByHs[server] = 1;
}
- // _semaphore.Release();
+
+ var resp = await SendStateEventAsync("m.space.child", room.RoomId, new {
+ via = memberCountByHs
+ .OrderByDescending(x => x.Value)
+ .Select(x => x.Key)
+ .Take(10)
+ });
+ return resp;
}
}
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index 49167fa..666d2a2 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -9,29 +9,29 @@ using Microsoft.Extensions.Logging;
namespace LibMatrix.Services;
public class HomeserverProviderService {
- private readonly TieredStorageService _tieredStorageService;
private readonly ILogger<HomeserverProviderService> _logger;
private readonly HomeserverResolverService _homeserverResolverService;
- public HomeserverProviderService(TieredStorageService tieredStorageService,
- ILogger<HomeserverProviderService> logger, HomeserverResolverService homeserverResolverService) {
- _tieredStorageService = tieredStorageService;
+ public HomeserverProviderService(ILogger<HomeserverProviderService> logger, HomeserverResolverService homeserverResolverService) {
_logger = logger;
_homeserverResolverService = homeserverResolverService;
- logger.LogDebug("New HomeserverProviderService created with TieredStorageService<{}>!",
- string.Join(", ", tieredStorageService.GetType().GetProperties().Select(x => x.Name)));
}
private static Dictionary<string, SemaphoreSlim> _authenticatedHomeserverSemaphore = new();
private static Dictionary<string, AuthenticatedHomeserverGeneric> _authenticatedHomeServerCache = new();
+ private static Dictionary<string, SemaphoreSlim> _remoteHomeserverSemaphore = new();
+ private static Dictionary<string, RemoteHomeServer> _remoteHomeServerCache = new();
+
public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedWithToken(string homeserver, string accessToken,
string? proxy = null) {
var sem = _authenticatedHomeserverSemaphore.GetOrCreate(homeserver + accessToken, _ => new SemaphoreSlim(1, 1));
await sem.WaitAsync();
- if (_authenticatedHomeServerCache.ContainsKey(homeserver + accessToken)) {
- sem.Release();
- return _authenticatedHomeServerCache[homeserver + accessToken];
+ lock (_authenticatedHomeServerCache) {
+ if (_authenticatedHomeServerCache.ContainsKey(homeserver + accessToken)) {
+ sem.Release();
+ return _authenticatedHomeServerCache[homeserver + accessToken];
+ }
}
var domain = proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
@@ -45,12 +45,11 @@ public class HomeserverProviderService {
hs = new AuthenticatedHomeserverGeneric(homeserver, accessToken);
}
- hs.FullHomeServerDomain = domain;
hs._httpClient = hc;
hs._httpClient.Timeout = TimeSpan.FromMinutes(15);
hs._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
- hs.WhoAmI = (await hs._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!;
+ // (() => hs.WhoAmI) = (await hs._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!;
lock(_authenticatedHomeServerCache)
_authenticatedHomeServerCache[homeserver + accessToken] = hs;
@@ -60,11 +59,10 @@ public class HomeserverProviderService {
}
public async Task<RemoteHomeServer> GetRemoteHomeserver(string homeserver, string? proxy = null) {
- var hs = new RemoteHomeServer(homeserver);
- hs.FullHomeServerDomain = proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
- hs._httpClient.Dispose();
- hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
- hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
+ var hs = new RemoteHomeServer(proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver));
+ // hs._httpClient.Dispose();
+ // hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
+ // hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
return hs;
}
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index 97348a5..b42bd64 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -2,6 +2,7 @@ using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using ArcaneLibs;
using ArcaneLibs.Extensions;
using LibMatrix.EventTypes;
@@ -48,7 +49,7 @@ public class StateEvent {
return null;
}
- set => RawContent = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(value));
+ set => RawContent = JsonSerializer.Deserialize<JsonObject>(JsonSerializer.Serialize(value, value.GetType()));
}
[JsonPropertyName("state_key")]
@@ -120,3 +121,32 @@ public class StateEvent {
[JsonIgnore]
public string cdtype => TypedContent.GetType().Name;
}
+
+/*
+public class StateEventContentPolymorphicTypeInfoResolver : DefaultJsonTypeInfoResolver
+{
+ public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
+ {
+ JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
+
+ Type baseType = typeof(EventContent);
+ if (jsonTypeInfo.Type == baseType) {
+ jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions {
+ TypeDiscriminatorPropertyName = "type",
+ IgnoreUnrecognizedTypeDiscriminators = true,
+ UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType,
+
+ DerivedTypes = StateEvent.KnownStateEventTypesByName.Select(x => new JsonDerivedType(x.Value, x.Key)).ToList()
+
+ // DerivedTypes = new ClassCollector<EventContent>()
+ // .ResolveFromAllAccessibleAssemblies()
+ // .SelectMany(t => t.GetCustomAttributes<MatrixEventAttribute>()
+ // .Select(a => new JsonDerivedType(t, attr.EventName));
+
+ };
+ }
+
+ return jsonTypeInfo;
+ }
+}
+*/
|