diff --git a/LibMatrix.EventTypes/Spec/IgnoredUserListEventContent.cs b/LibMatrix.EventTypes/Spec/IgnoredUserListEventContent.cs
new file mode 100644
index 0000000..3643b8c
--- /dev/null
+++ b/LibMatrix.EventTypes/Spec/IgnoredUserListEventContent.cs
@@ -0,0 +1,27 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+
+namespace LibMatrix.EventTypes.Spec;
+
+[MatrixEvent(EventName = EventId)]
+public class IgnoredUserListEventContent : EventContent {
+ public const string EventId = "m.ignored_user_list";
+
+ [JsonPropertyName("ignored_users")]
+ public Dictionary<string, IgnoredUserContent> IgnoredUsers { get; set; } = new();
+
+ // Dummy type to provide easy access to the by-spec empty content
+ public class IgnoredUserContent {
+ [JsonExtensionData]
+ public Dictionary<string, object>? AdditionalData { get; set; } = [];
+
+ public T GetAdditionalData<T>(string key) {
+ if (AdditionalData == null || !AdditionalData.TryGetValue(key, out var value))
+ throw new KeyNotFoundException($"Key '{key}' not found in AdditionalData.");
+ if (value is T tValue)
+ return tValue;
+ throw new InvalidCastException($"Value for key '{key}' cannot be cast to type '{typeof(T)}'. Cannot continue.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
index 0cb4a25..6f8c194 100644
--- a/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
+++ b/LibMatrix.EventTypes/Spec/State/Policy/PolicyRuleStateEventContent.cs
@@ -97,16 +97,27 @@ public abstract class PolicyRuleEventContent : EventContent {
public Regex? GetEntityRegex() => Entity is null ? null : new(Entity.Replace(".", "\\.").Replace("*", ".*").Replace("?", "."));
+ public bool IsGlobRule() =>
+ Entity != null
+ && (Entity.Contains('*') || Entity.Contains('?'));
+
public bool EntityMatches(string entity) =>
Entity != null
&& (
Entity == entity
|| (
- Entity.Contains("*") || Entity.Contains("?")
+ IsGlobRule()
? GetEntityRegex()!.IsMatch(entity)
: entity == Entity
)
);
+
+ public string? GetNormalizedRecommendation() {
+ if (Recommendation is "m.ban" or "org.matrix.mjolnir.ban")
+ return PolicyRecommendationTypes.Ban;
+
+ return Recommendation;
+ }
}
public static class PolicyRecommendationTypes {
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index c1bbc5a..55899de 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -5,6 +5,7 @@ using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Web;
using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Spec;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Filters;
using LibMatrix.Helpers;
@@ -169,8 +170,9 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
try {
return await GetAccountDataAsync<T>(key);
}
- catch (Exception e) {
- return default;
+ catch (MatrixException e) {
+ if (e is { ErrorCode: MatrixException.ErrorCodes.M_NOT_FOUND }) return default;
+ throw;
}
}
@@ -188,8 +190,7 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
public async Task UpdateProfilePropertyAsync(string name, object? value) {
var caps = await GetCapabilitiesAsync();
- if(caps is null) throw new Exception("Failed to get capabilities");
-
+ if (caps is null) throw new Exception("Failed to get capabilities");
}
#endregion
@@ -532,8 +533,49 @@ public class AuthenticatedHomeserverGeneric : RemoteHomeserver {
}
#endregion
+
+ public Task ReportRoomAsync(string roomId, string reason) =>
+ ClientHttpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{roomId}/report", new {
+ reason
+ });
+
+ public async Task ReportRoomEventAsync(string roomId, string eventId, string reason, int score = 0, bool ignoreSender = false) {
+ await ClientHttpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{roomId}/report/{eventId}", new {
+ reason,
+ score
+ });
+
+ if (ignoreSender) {
+ var eventContent = await GetRoom(roomId).GetEventAsync(eventId);
+ var sender = eventContent.Sender;
+ await IgnoreUserAsync(sender);
+ }
+ }
+
+ public async Task ReportUserAsync(string userId, string reason, bool ignore = false) {
+ await ClientHttpClient.PostAsJsonAsync($"/_matrix/client/v3/users/{userId}/report", new {
+ reason
+ });
+
+ if (ignore) {
+ await IgnoreUserAsync(userId);
+ }
+ }
+
+ public async Task<IgnoredUserListEventContent> GetIgnoredUserListAsync() {
+ return await GetAccountDataOrNullAsync<IgnoredUserListEventContent>(IgnoredUserListEventContent.EventId) ?? new();
+ }
+
+ public async Task IgnoreUserAsync(string userId, IgnoredUserListEventContent.IgnoredUserContent? content = null) {
+ content ??= new();
+
+ var ignoredUserList = await GetIgnoredUserListAsync();
+ ignoredUserList.IgnoredUsers.TryAdd(userId, content);
+ await SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoredUserList);
+ }
+
private class CapabilitiesResponse {
[JsonPropertyName("capabilities")]
public Dictionary<string, object>? Capabilities { get; set; }
}
-}
+}
\ No newline at end of file
|