diff --git a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
index 0569477..86950e5 100644
--- a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
@@ -43,7 +43,7 @@ public abstract class PolicyRuleEventContent : EventContent {
[FriendlyName(Name = "Entity")]
public string? Entity { get; set; }
- private bool init;
+ // private bool init;
/// <summary>
/// Reason this user is banned
@@ -93,13 +93,18 @@ public abstract class PolicyRuleEventContent : EventContent {
public string GetDraupnir2StateKey() => Convert.ToBase64String(SHA256.HashData($"{Entity}{Recommendation}".AsBytes().ToArray()));
- public Regex GetEntityRegex() => new(Entity.Replace(".", "\\.").Replace("*", ".*").Replace("?", "."));
+ public Regex? GetEntityRegex() => Entity is null ? null : new(Entity.Replace(".", "\\.").Replace("*", ".*").Replace("?", "."));
public bool EntityMatches(string entity) =>
- Entity == entity
- || (Entity.Contains("*") || Entity.Contains("?")
- ? GetEntityRegex().IsMatch(entity)
- : entity == Entity);
+ Entity != null
+ && (
+ Entity == entity
+ || (
+ Entity.Contains("*") || Entity.Contains("?")
+ ? GetEntityRegex()!.IsMatch(entity)
+ : entity == Entity
+ )
+ );
}
public static class PolicyRecommendationTypes {
diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomCreateEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomCreateEventContent.cs
index 039c61f..37b831a 100644
--- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomCreateEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomCreateEventContent.cs
@@ -1,8 +1,11 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace LibMatrix.EventTypes.Spec.State.RoomInfo;
[MatrixEvent(EventName = EventId)]
+[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global", Justification = "Deserialization, public API")]
+[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global", Justification = "Deserialization, public API")]
public class RoomCreateEventContent : EventContent {
public const string EventId = "m.room.create";
@@ -20,12 +23,12 @@ public class RoomCreateEventContent : EventContent {
[JsonPropertyName("type")]
public string? Type { get; set; }
+}
- public class RoomCreatePredecessor {
- [JsonPropertyName("room_id")]
- public string? RoomId { get; set; }
+public class RoomCreatePredecessor {
+ [JsonPropertyName("room_id")]
+ public string? RoomId { get; set; }
- [JsonPropertyName("event_id")]
- public string? EventId { get; set; }
- }
+ [JsonPropertyName("event_id")]
+ public string? EventId { get; set; }
}
\ No newline at end of file
diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs
index ee1e218..c9aa603 100644
--- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomPowerLevelEventContent.cs
@@ -20,7 +20,7 @@ public class RoomPowerLevelEventContent : EventContent {
public long? Kick { get; set; } = 50;
[JsonPropertyName("notifications")]
- public NotificationsPL? NotificationsPl { get; set; } // = null!;
+ public NotificationsPowerLevels? NotificationsPl { get; set; } // = null!;
[JsonPropertyName("redact")]
public long? Redact { get; set; } = 50;
@@ -42,19 +42,19 @@ public class RoomPowerLevelEventContent : EventContent {
[JsonPropertyName("historical")]
public long Historical { get; set; } // = 50;
- public class NotificationsPL {
+ public class NotificationsPowerLevels {
[JsonPropertyName("room")]
public long Room { get; set; } = 50;
}
public bool IsUserAdmin(string userId) {
ArgumentNullException.ThrowIfNull(userId);
- return Users.TryGetValue(userId, out var level) && level >= Events.Max(x => x.Value);
+ return GetUserPowerLevel(userId) >= Events?.Max(x => x.Value);
}
public bool UserHasTimelinePermission(string userId, string eventType) {
ArgumentNullException.ThrowIfNull(userId);
- return Users.TryGetValue(userId, out var level) && level >= Events.GetValueOrDefault(eventType, EventsDefault ?? 0);
+ return GetUserPowerLevel(userId) >= Events?.GetValueOrDefault(eventType, EventsDefault ?? 0);
}
public bool UserHasStatePermission(string userId, string eventType, bool log = false) {
diff --git a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs
index cb958ae..e3fc892 100644
--- a/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/RoomInfo/RoomServerACLEventContent.cs
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
namespace LibMatrix.EventTypes.Spec.State.RoomInfo;
[MatrixEvent(EventName = EventId)]
-public class RoomServerACLEventContent : EventContent {
+public class RoomServerAclEventContent : EventContent {
public const string EventId = "m.room.server_acl";
[JsonPropertyName("allow")]
diff --git a/LibMatrix/Extensions/CanonicalJsonSerializer.cs b/LibMatrix/Extensions/CanonicalJsonSerializer.cs
index c9d38cd..55a4b1a 100644
--- a/LibMatrix/Extensions/CanonicalJsonSerializer.cs
+++ b/LibMatrix/Extensions/CanonicalJsonSerializer.cs
@@ -8,7 +8,7 @@ namespace LibMatrix.Extensions;
public static class CanonicalJsonSerializer {
// TODO: Alphabetise dictionaries
- private static JsonSerializerOptions _options => new() {
+ private static JsonSerializerOptions JsonOptions => new() {
WriteIndented = false,
Encoder = UnicodeJsonEncoder.Singleton,
};
@@ -19,7 +19,7 @@ public static class CanonicalJsonSerializer {
.ToFrozenSet();
private static JsonSerializerOptions MergeOptions(JsonSerializerOptions? inputOptions) {
- var newOptions = _options;
+ var newOptions = JsonOptions;
if (inputOptions == null)
return newOptions;
@@ -43,7 +43,7 @@ public static class CanonicalJsonSerializer {
public static String Serialize<TValue>(TValue value, JsonSerializerOptions? options = null) {
var newOptions = MergeOptions(options);
- return System.Text.Json.JsonSerializer.SerializeToNode(value, options) // We want to allow passing custom converters for eg. double/float -> string here...
+ return JsonSerializer.SerializeToNode(value, options) // We want to allow passing custom converters for eg. double/float -> string here...
.SortProperties()!
.CanonicalizeNumbers()!
.ToJsonString(newOptions);
@@ -53,13 +53,14 @@ public static class CanonicalJsonSerializer {
}
- public static String Serialize(object value, Type inputType, JsonSerializerOptions? options = null) => JsonSerializer.Serialize(value, inputType, _options);
+ public static String Serialize(object value, Type inputType, JsonSerializerOptions? options = null) => JsonSerializer.Serialize(value, inputType, JsonOptions);
// public static String Serialize<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo) => JsonSerializer.Serialize(value, jsonTypeInfo, _options);
// public static String Serialize(Object value, JsonTypeInfo jsonTypeInfo)
#endregion
- private static partial class JsonExtensions {
+ // ReSharper disable once UnusedType.Local
+ private static class JsonExtensions {
public static Action<JsonTypeInfo> AlphabetizeProperties(Type type) {
return typeInfo => {
if (typeInfo.Kind != JsonTypeInfoKind.Object || !type.IsAssignableFrom(typeInfo.Type))
diff --git a/LibMatrix/Extensions/JsonElementExtensions.cs b/LibMatrix/Extensions/JsonElementExtensions.cs
index c4ed743..dfec95b 100644
--- a/LibMatrix/Extensions/JsonElementExtensions.cs
+++ b/LibMatrix/Extensions/JsonElementExtensions.cs
@@ -126,6 +126,7 @@ public static class JsonElementExtensions {
$"Encountered dictionary {field.Name} with key type {keyType.Name} and value type {valueType.Name}!");
return field.Value.EnumerateObject()
+ // TODO: use key.Value?
.Where(key => !valueType.IsPrimitive && valueType != typeof(string))
.Aggregate(false, (current, key) =>
current | key.FindExtraJsonPropertyFieldsByValueKind(containerType, valueType)
diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
index 895217d..56925fb 100644
--- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs
+++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -76,7 +76,7 @@ public class MatrixHttpClient {
Console.WriteLine($"Sending {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.GetContentLength())})");
if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null");
- if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri);
+ if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress ?? throw new InvalidOperationException("Relative URI passed, but no BaseAddress is specified!"), request.RequestUri);
foreach (var (key, value) in AdditionalQueryParameters) request.RequestUri = request.RequestUri.AddQuery(key, value);
foreach (var (key, value) in DefaultRequestHeaders) {
if (request.Headers.Contains(key)) continue;
@@ -126,7 +126,7 @@ public class MatrixHttpClient {
if (!content.StartsWith('{')) throw new InvalidDataException("Encountered invalid data:\n" + content);
//we have a matrix error
- MatrixException? ex = null;
+ MatrixException? ex;
try {
ex = JsonSerializer.Deserialize<MatrixException>(content);
}
@@ -140,7 +140,7 @@ public class MatrixHttpClient {
Debug.Assert(ex != null, nameof(ex) + " != null");
ex.RawContent = content;
// Console.WriteLine($"Failed to send request: {ex}");
- if (ex?.RetryAfterMs is null) throw ex!;
+ if (ex.RetryAfterMs is null) throw ex!;
//we have a ratelimit error
await Task.Delay(ex.RetryAfterMs.Value, cancellationToken);
request.ResetSendStatus();
@@ -179,7 +179,7 @@ public class MatrixHttpClient {
}
// GetStreamAsync
- public new async Task<Stream> GetStreamAsync(string requestUri, CancellationToken cancellationToken = default) {
+ public async Task<Stream> GetStreamAsync(string requestUri, CancellationToken cancellationToken = default) {
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await SendAsync(request, cancellationToken);
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index aed56a7..05bfb47 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -1,12 +1,10 @@
using System.Diagnostics;
-using System.Net.Http.Json;
using System.Text.Json;
using ArcaneLibs.Collections;
using ArcaneLibs.Extensions;
using LibMatrix.Filters;
using LibMatrix.Homeservers;
using LibMatrix.Responses;
-using LibMatrix.Utilities;
using Microsoft.Extensions.Logging;
namespace LibMatrix.Helpers;
@@ -14,8 +12,8 @@ namespace LibMatrix.Helpers;
public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logger = null) {
private SyncFilter? _filter;
private string? _namedFilterName;
- private bool _filterIsDirty = false;
- private string? _filterId = null;
+ private bool _filterIsDirty;
+ private string? _filterId;
public string? Since { get; set; }
public int Timeout { get; set; } = 30000;
@@ -55,7 +53,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
public TimeSpan MinimumDelay { get; set; } = new(0);
- private async Task updateFilterAsync() {
+ private async Task UpdateFilterAsync() {
if (!string.IsNullOrWhiteSpace(NamedFilterName)) {
_filterId = await homeserver.NamedCaches.FilterCache.GetOrSetValueAsync(NamedFilterName);
if (_filterId is null)
@@ -79,7 +77,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
}
var sw = Stopwatch.StartNew();
- if (_filterIsDirty) await updateFilterAsync();
+ if (_filterIsDirty) await UpdateFilterAsync();
var url = $"/_matrix/client/v3/sync?timeout={Timeout}&set_presence={SetPresence}&full_state={(FullState ? "true" : "false")}";
if (!string.IsNullOrWhiteSpace(Since)) url += $"&since={Since}";
@@ -189,7 +187,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
if (syncResponse.Rooms is { Join.Count: > 0 })
foreach (var updatedRoom in syncResponse.Rooms.Join) {
if (updatedRoom.Value.Timeline is null) continue;
- foreach (var stateEventResponse in updatedRoom.Value.Timeline.Events) {
+ foreach (var stateEventResponse in updatedRoom.Value.Timeline.Events ?? []) {
stateEventResponse.RoomId = updatedRoom.Key;
var tasks = TimelineEventHandlers.Select(x => x(stateEventResponse)).ToList();
await Task.WhenAll(tasks);
@@ -216,9 +214,4 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
/// Event fired when an account data event is received
/// </summary>
public List<Func<StateEventResponse, Task>> AccountDataReceivedHandlers { get; } = new();
-
- private void Log(string message) {
- if (logger is null) Console.WriteLine(message);
- else logger.LogInformation(message);
- }
}
\ No newline at end of file
diff --git a/LibMatrix/Helpers/SyncStateResolver.cs b/LibMatrix/Helpers/SyncStateResolver.cs
index 72d600d..4633f06 100644
--- a/LibMatrix/Helpers/SyncStateResolver.cs
+++ b/LibMatrix/Helpers/SyncStateResolver.cs
@@ -27,14 +27,13 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
// run sync
var sync = await _syncHelper.SyncAsync(cancellationToken);
if (sync is null) return await ContinueAsync(cancellationToken);
- if (MergedState is null) MergedState = sync;
- else MergedState = MergeSyncs(MergedState, sync);
+ MergedState = MergedState is null ? sync : MergeSyncs(MergedState, sync);
Since = sync.NextBatch;
return (sync, MergedState);
}
private SyncResponse MergeSyncs(SyncResponse oldState, SyncResponse newState) {
- oldState.NextBatch = newState.NextBatch ?? oldState.NextBatch;
+ oldState.NextBatch = newState.NextBatch;
oldState.AccountData ??= new EventList();
oldState.AccountData.Events ??= new List<StateEventResponse>();
@@ -42,8 +41,11 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
oldState.AccountData.Events.MergeStateEventLists(newState.AccountData?.Events ?? new List<StateEventResponse>());
oldState.Presence ??= new SyncResponse.PresenceDataStructure();
- if (newState.Presence?.Events is not null)
- oldState.Presence.Events.MergeStateEventLists(newState.Presence?.Events ?? new List<StateEventResponse>());
+ if (newState.Presence?.Events is { Count: > 0 })
+ if (oldState.Presence.Events is { Count: > 0 })
+ oldState.Presence.Events.MergeStateEventLists(newState.Presence.Events);
+ else
+ oldState.Presence.Events = newState.Presence?.Events;
oldState.DeviceOneTimeKeysCount ??= new Dictionary<string, int>();
if (newState.DeviceOneTimeKeysCount is not null)
@@ -60,12 +62,17 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
oldState.ToDevice.Events.MergeStateEventLists(newState.ToDevice?.Events ?? new List<StateEventResponse>());
oldState.DeviceLists ??= new SyncResponse.DeviceListsDataStructure();
- if (newState.DeviceLists?.Changed is not null)
- foreach (var s in oldState.DeviceLists.Changed!)
+ if (newState.DeviceLists?.Changed is not null) {
+ oldState.DeviceLists.Changed ??= new List<string>();
+ foreach (var s in newState.DeviceLists.Changed)
oldState.DeviceLists.Changed.Add(s);
- if (newState.DeviceLists?.Left is not null)
- foreach (var s in oldState.DeviceLists.Left!)
+ }
+
+ if (newState.DeviceLists?.Left is not null) {
+ oldState.DeviceLists.Left ??= new List<string>();
+ foreach (var s in newState.DeviceLists.Left)
oldState.DeviceLists.Left.Add(s);
+ }
return oldState;
}
@@ -75,18 +82,18 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
private SyncResponse.RoomsDataStructure MergeRoomsDataStructure(SyncResponse.RoomsDataStructure oldState, SyncResponse.RoomsDataStructure newState) {
oldState.Join ??= new Dictionary<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure>();
foreach (var (key, value) in newState.Join ?? new Dictionary<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure>())
- if (!oldState.Join.ContainsKey(key)) oldState.Join[key] = value;
- else oldState.Join[key] = MergeJoinedRoomDataStructure(oldState.Join[key], value);
+ if (!oldState.Join.TryAdd(key, value))
+ oldState.Join[key] = MergeJoinedRoomDataStructure(oldState.Join[key], value);
oldState.Invite ??= new Dictionary<string, SyncResponse.RoomsDataStructure.InvitedRoomDataStructure>();
foreach (var (key, value) in newState.Invite ?? new Dictionary<string, SyncResponse.RoomsDataStructure.InvitedRoomDataStructure>())
- if (!oldState.Invite.ContainsKey(key)) oldState.Invite[key] = value;
- else oldState.Invite[key] = MergeInvitedRoomDataStructure(oldState.Invite[key], value);
+ if (!oldState.Invite.TryAdd(key, value))
+ oldState.Invite[key] = MergeInvitedRoomDataStructure(oldState.Invite[key], value);
oldState.Leave ??= new Dictionary<string, SyncResponse.RoomsDataStructure.LeftRoomDataStructure>();
foreach (var (key, value) in newState.Leave ?? new Dictionary<string, SyncResponse.RoomsDataStructure.LeftRoomDataStructure>()) {
- if (!oldState.Leave.ContainsKey(key)) oldState.Leave[key] = value;
- else oldState.Leave[key] = MergeLeftRoomDataStructure(oldState.Leave[key], value);
+ if (!oldState.Leave.TryAdd(key, value))
+ oldState.Leave[key] = MergeLeftRoomDataStructure(oldState.Leave[key], value);
if (oldState.Invite.ContainsKey(key)) oldState.Invite.Remove(key);
if (oldState.Join.ContainsKey(key)) oldState.Join.Remove(key);
}
@@ -157,9 +164,9 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
oldData.UnreadNotifications.NotificationCount = newData.UnreadNotifications?.NotificationCount ?? oldData.UnreadNotifications.NotificationCount;
oldData.Summary ??= new SyncResponse.RoomsDataStructure.JoinedRoomDataStructure.SummaryDataStructure {
- Heroes = newData.Summary?.Heroes ?? oldData.Summary.Heroes,
- JoinedMemberCount = newData.Summary?.JoinedMemberCount ?? oldData.Summary.JoinedMemberCount,
- InvitedMemberCount = newData.Summary?.InvitedMemberCount ?? oldData.Summary.InvitedMemberCount
+ Heroes = newData.Summary?.Heroes ?? oldData.Summary?.Heroes,
+ JoinedMemberCount = newData.Summary?.JoinedMemberCount ?? oldData.Summary?.JoinedMemberCount,
+ InvitedMemberCount = newData.Summary?.InvitedMemberCount ?? oldData.Summary?.InvitedMemberCount
};
oldData.Summary.Heroes = newData.Summary?.Heroes ?? oldData.Summary.Heroes;
oldData.Summary.JoinedMemberCount = newData.Summary?.JoinedMemberCount ?? oldData.Summary.JoinedMemberCount;
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index 40d7fb4..f47ab64 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -375,11 +375,11 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
/// <returns>All account data.</returns>
/// <exception cref="Exception"></exception>
public async Task<EventList?> EnumerateAccountData() {
- var syncHelper = new SyncHelper(this);
- syncHelper.FilterId = await NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetAccountData);
+ var syncHelper = new SyncHelper(this) {
+ FilterId = await NamedCaches.FilterCache.GetOrSetValueAsync(CommonSyncFilters.GetAccountData)
+ };
var resp = await syncHelper.SyncAsync();
- if (resp is null) throw new Exception("Sync failed");
- return resp.AccountData;
+ return resp?.AccountData ?? throw new Exception("Sync failed");
}
private Dictionary<string, string>? _namedFilterCache;
@@ -431,7 +431,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
//fallback to legacy media
try {
- var uri = $"/_matrix/media/v1/download/{serverName}/{mediaId}";
+ var uri = $"/_matrix/media/v3/download/{serverName}/{mediaId}";
if (!string.IsNullOrWhiteSpace(filename)) uri += $"/{HttpUtility.UrlEncode(filename)}";
if (timeout is not null) uri += $"?timeout_ms={timeout}";
var res = await ClientHttpClient.GetAsync(uri);
@@ -455,7 +455,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
uri = uri.AddQuery("width", width.ToString());
uri = uri.AddQuery("height", height.ToString());
if (!string.IsNullOrWhiteSpace(method)) uri = uri.AddQuery("method", method);
- if (timeout is not null) uri = uri.AddQuery("timeout_ms", timeout.ToString());
+ if (timeout is not null) uri = uri.AddQuery("timeout_ms", timeout.ToString()!);
var res = await ClientHttpClient.GetAsync(uri.ToString());
return await res.Content.ReadAsStreamAsync();
@@ -466,11 +466,11 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
//fallback to legacy media
try {
- var uri = new Uri($"/_matrix/media/v1/thumbnail/{serverName}/{mediaId}");
+ var uri = new Uri($"/_matrix/media/v3/thumbnail/{serverName}/{mediaId}");
uri = uri.AddQuery("width", width.ToString());
uri = uri.AddQuery("height", height.ToString());
if (!string.IsNullOrWhiteSpace(method)) uri = uri.AddQuery("method", method);
- if (timeout is not null) uri = uri.AddQuery("timeout_ms", timeout.ToString());
+ if (timeout is not null) uri = uri.AddQuery("timeout_ms", timeout.ToString()!);
var res = await ClientHttpClient.GetAsync(uri.ToString());
return await res.Content.ReadAsStreamAsync();
@@ -497,7 +497,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
//fallback to legacy media
try {
- var res = await ClientHttpClient.GetAsync($"/_matrix/media/v1/preview_url?url={HttpUtility.UrlEncode(url)}");
+ var res = await ClientHttpClient.GetAsync($"/_matrix/media/v3/preview_url?url={HttpUtility.UrlEncode(url)}");
return await res.Content.ReadFromJsonAsync<Dictionary<string, JsonValue>>();
}
catch (MatrixException e) {
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index ee70dc3..7995f03 100644
--- a/LibMatrix/Homeservers/RemoteHomeServer.cs
+++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -49,7 +49,7 @@ 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;
}
@@ -58,7 +58,7 @@ public class RemoteHomeserver {
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) {
@@ -66,7 +66,7 @@ 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}");
}
#region Authentication
@@ -78,12 +78,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) {
@@ -99,18 +98,13 @@ 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;
}
diff --git a/LibMatrix/LibMatrixException.cs b/LibMatrix/LibMatrixException.cs
index 5854826..27cfc2a 100644
--- a/LibMatrix/LibMatrixException.cs
+++ b/LibMatrix/LibMatrixException.cs
@@ -1,5 +1,7 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using ArcaneLibs.Extensions;
+// ReSharper disable MemberCanBePrivate.Global
namespace LibMatrix;
@@ -20,6 +22,7 @@ public class LibMatrixException : Exception {
_ => $"Unknown error: {GetAsObject().ToJson(ignoreNull: true)}"
}}\nError: {Error}";
+ [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Follows spec naming")]
public static class ErrorCodes {
public const string M_NOT_FOUND = "M_NOT_FOUND";
public const string M_UNSUPPORTED = "M_UNSUPPORTED";
diff --git a/LibMatrix/MatrixException.cs b/LibMatrix/MatrixException.cs
index eb207da..3a79af8 100644
--- a/LibMatrix/MatrixException.cs
+++ b/LibMatrix/MatrixException.cs
@@ -1,5 +1,7 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using ArcaneLibs.Extensions;
+// ReSharper disable MemberCanBePrivate.Global
namespace LibMatrix;
@@ -17,7 +19,7 @@ public class MatrixException : Exception {
[JsonPropertyName("retry_after_ms")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? RetryAfterMs { get; set; }
-
+
public string RawContent { get; set; }
public object GetAsObject() => new { errcode = ErrorCode, error = Error, soft_logout = SoftLogout, retry_after_ms = RetryAfterMs };
@@ -62,6 +64,7 @@ public class MatrixException : Exception {
_ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
}}";
+ [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Follows spec naming")]
public static class ErrorCodes {
public const string M_FORBIDDEN = "M_FORBIDDEN";
public const string M_UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN";
diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs
index 38bc970..741bea1 100644
--- a/LibMatrix/Responses/CreateRoomRequest.cs
+++ b/LibMatrix/Responses/CreateRoomRequest.cs
@@ -36,10 +36,11 @@ public class CreateRoomRequest {
/// One of: ["public", "private"]
/// </summary>
[JsonPropertyName("visibility")]
+ // ReSharper disable once UnusedAutoPropertyAccessor.Global
public string? Visibility { get; set; }
[JsonPropertyName("power_level_content_override")]
- public RoomPowerLevelEventContent? PowerLevelContentOverride { get; set; } = null!;
+ public RoomPowerLevelEventContent? PowerLevelContentOverride { get; set; }
[JsonPropertyName("creation_content")]
public JsonObject CreationContent { get; set; } = new();
@@ -51,11 +52,11 @@ public class CreateRoomRequest {
/// For use only when you can't use the CreationContent property
/// </summary>
- public StateEvent this[string eventType, string eventKey = ""] {
+ public StateEvent? this[string eventType, string eventKey = ""] {
get {
- var stateEvent = InitialState.FirstOrDefault(x => x.Type == eventType && x.StateKey == eventKey);
+ var stateEvent = InitialState?.FirstOrDefault(x => x.Type == eventType && x.StateKey == eventKey);
if (stateEvent == null)
- InitialState.Add(stateEvent = new StateEvent {
+ InitialState?.Add(stateEvent = new StateEvent {
Type = eventType,
StateKey = eventKey,
TypedContent = (EventContent)Activator.CreateInstance(
@@ -78,7 +79,7 @@ public class CreateRoomRequest {
public Dictionary<string, string> Validate() {
Dictionary<string, string> errors = new();
- if (!Regex.IsMatch(RoomAliasName, @"[a-zA-Z0-9_\-]+$"))
+ if (!string.IsNullOrWhiteSpace(RoomAliasName) && !Regex.IsMatch(RoomAliasName, @"[a-zA-Z0-9_\-]+$"))
errors.Add("room_alias_name",
"Room alias name must only contain letters, numbers, underscores, and hyphens.");
@@ -98,7 +99,7 @@ public class CreateRoomRequest {
Invite = 25,
StateDefault = 10,
Redact = 50,
- NotificationsPl = new RoomPowerLevelEventContent.NotificationsPL {
+ NotificationsPl = new RoomPowerLevelEventContent.NotificationsPowerLevels {
Room = 10
},
Events = new Dictionary<string, long> {
@@ -138,7 +139,7 @@ public class CreateRoomRequest {
Invite = 25,
StateDefault = 10,
Redact = 50,
- NotificationsPl = new RoomPowerLevelEventContent.NotificationsPL {
+ NotificationsPl = new RoomPowerLevelEventContent.NotificationsPowerLevels {
Room = 10
},
Events = new Dictionary<string, long> {
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index b8e68f8..14276d7 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -135,7 +135,7 @@ public class GenericRoom {
return await GetStateEventAsync(type, stateKey);
}
catch (MatrixException e) {
- if (e.ErrorCode == "M_NOT_FOUND") return default;
+ if (e.ErrorCode == "M_NOT_FOUND") return null;
throw;
}
}
@@ -237,10 +237,11 @@ public class GenericRoom {
var result = await JsonSerializer.DeserializeAsync<ChunkedStateEventResponse>(await res.Content.ReadAsStreamAsync(), new JsonSerializerOptions() {
TypeInfoResolver = ChunkedStateEventResponseSerializerContext.Default
});
+ if (result is null) throw new Exception("Failed to deserialise members response");
// if (sw.ElapsedMilliseconds > 100)
// Console.WriteLine($"Members call deserialised in {sw.GetElapsedAndRestart()}");
// else sw.Restart();
- foreach (var resp in result.Chunk) {
+ foreach (var resp in result.Chunk ?? []) {
if (resp?.Type != "m.room.member") continue;
if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue;
yield return resp;
@@ -261,11 +262,12 @@ public class GenericRoom {
var result = await JsonSerializer.DeserializeAsync<ChunkedStateEventResponse>(await res.Content.ReadAsStreamAsync(), new JsonSerializerOptions() {
TypeInfoResolver = ChunkedStateEventResponseSerializerContext.Default
});
+ if (result is null) throw new Exception("Failed to deserialise members response");
// if (sw.ElapsedMilliseconds > 100)
// Console.WriteLine($"Members call deserialised in {sw.GetElapsedAndRestart()}");
// else sw.Restart();
var members = new List<StateEventResponse>();
- foreach (var resp in result.Chunk) {
+ foreach (var resp in result.Chunk ?? []) {
if (resp?.Type != "m.room.member") continue;
if (joinedOnly && resp.RawContent?["membership"]?.GetValue<string>() != "join") continue;
members.Add(resp);
@@ -428,7 +430,7 @@ public class GenericRoom {
return await res.Content.ReadFromJsonAsync<T>();
}
-
+
public async Task<T?> GetRoomAccountDataOrNullAsync<T>(string key) {
try {
return await GetRoomAccountDataAsync<T>(key);
@@ -456,7 +458,8 @@ public class GenericRoom {
while (true) {
try {
return (await (await Homeserver.ClientHttpClient.PutAsJsonAsync(url, data)).Content.ReadFromJsonAsync<EventIdResponse>())!;
- } catch (MatrixException e) {
+ }
+ catch (MatrixException e) {
if (e is { ErrorCode: MatrixException.ErrorCodes.M_FORBIDDEN }) throw;
throw;
}
diff --git a/Utilities/LibMatrix.DevTestBot/Bot/Interfaces/CommandContext.cs b/Utilities/LibMatrix.DevTestBot/Bot/Interfaces/CommandContext.cs
index 90a95e4..221d1f4 100644
--- a/Utilities/LibMatrix.DevTestBot/Bot/Interfaces/CommandContext.cs
+++ b/Utilities/LibMatrix.DevTestBot/Bot/Interfaces/CommandContext.cs
@@ -4,8 +4,9 @@ using LibMatrix.RoomTypes;
namespace LibMatrix.ExampleBot.Bot.Interfaces;
public class CommandContext {
- public GenericRoom Room { get; set; }
- public StateEventResponse MessageEvent { get; set; }
- public string CommandName => (MessageEvent.TypedContent as RoomMessageEventContent).Body.Split(' ')[0][1..];
- public string[] Args => (MessageEvent.TypedContent as RoomMessageEventContent).Body.Split(' ')[1..];
+ public required GenericRoom Room { get; init; }
+ public required StateEventResponse MessageEvent { get; init; }
+ public string CommandName => MessageContent.Body.Split(' ')[0][1..];
+ public string[] Args => MessageContent.Body.Split(' ')[1..];
+ private RoomMessageEventContent MessageContent => MessageEvent.TypedContent as RoomMessageEventContent ?? throw new Exception("Message content is not a RoomMessageEventContent");
}
\ No newline at end of file
diff --git a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs
index c24e6e9..0571d5a 100644
--- a/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs
+++ b/Utilities/LibMatrix.HomeserverEmulator/Controllers/Rooms/RoomsController.cs
@@ -62,7 +62,7 @@ public class RoomsController(ILogger<RoomsController> logger, TokenService token
var room = new RoomStore.Room($"!{Guid.NewGuid()}:{tokenService.GenerateServerName(HttpContext)}");
var eventTypesToTransfer = new[] {
- RoomServerACLEventContent.EventId,
+ RoomServerAclEventContent.EventId,
RoomEncryptionEventContent.EventId,
RoomNameEventContent.EventId,
RoomAvatarEventContent.EventId,
|