about summary refs log tree commit diff
path: root/LibMatrix
diff options
context:
space:
mode:
authorTheArcaneBrony <myrainbowdash949@gmail.com>2023-09-05 06:28:52 +0200
committerTheArcaneBrony <myrainbowdash949@gmail.com>2023-09-05 06:28:52 +0200
commitcf455ed8de20bbee011289223e7d8d5775dfd69e (patch)
treecbdfdbc207af64a105b4d21941a6f0e71ca65e9d /LibMatrix
parentAdd start of Media Moderator PoC bot (diff)
downloadLibMatrix-cf455ed8de20bbee011289223e7d8d5775dfd69e.tar.xz
Media moderator PoC works, abstract command handling to library
Diffstat (limited to 'LibMatrix')
-rw-r--r--LibMatrix/Extensions/HttpClientExtensions.cs5
-rw-r--r--LibMatrix/Extensions/JsonElementExtensions.cs3
-rw-r--r--LibMatrix/Filters/SyncFilter.cs1
-rw-r--r--LibMatrix/Helpers/MatrixEventAttribute.cs2
-rw-r--r--LibMatrix/Helpers/MediaResolver.cs7
-rw-r--r--LibMatrix/Helpers/MessageFormatter.cs39
-rw-r--r--LibMatrix/Helpers/SyncHelper.cs34
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs6
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs2
-rw-r--r--LibMatrix/Homeservers/RemoteHomeServer.cs4
-rw-r--r--LibMatrix/Interfaces/Services/IStorageProvider.cs5
-rw-r--r--LibMatrix/LibMatrix.csproj1
-rw-r--r--LibMatrix/MatrixException.cs2
-rw-r--r--LibMatrix/MessagesResponse.cs1
-rw-r--r--LibMatrix/Responses/Admin/AdminRoomListingResult.cs1
-rw-r--r--LibMatrix/Responses/ClientVersionsResponse.cs1
-rw-r--r--LibMatrix/Responses/CreateRoomRequest.cs6
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs46
-rw-r--r--LibMatrix/RoomTypes/SpaceRoom.cs3
-rw-r--r--LibMatrix/Services/HomeserverProviderService.cs6
-rw-r--r--LibMatrix/Services/HomeserverResolverService.cs32
-rw-r--r--LibMatrix/Services/ServiceInstaller.cs2
-rw-r--r--LibMatrix/StateEvent.cs4
-rw-r--r--LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs2
-rw-r--r--LibMatrix/StateEventTypes/Spec/CanonicalAliasEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs2
-rw-r--r--LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs2
-rw-r--r--LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs2
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs16
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs4
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs2
-rw-r--r--LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs1
-rw-r--r--LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs1
45 files changed, 117 insertions, 141 deletions
diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs
index 4d81b6e..d4017ed 100644
--- a/LibMatrix/Extensions/HttpClientExtensions.cs
+++ b/LibMatrix/Extensions/HttpClientExtensions.cs
@@ -1,12 +1,7 @@
-using System;
 using System.Diagnostics;
-using System.IO;
-using System.Net.Http;
 using System.Net.Http.Headers;
 using System.Reflection;
 using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace LibMatrix.Extensions;
 
diff --git a/LibMatrix/Extensions/JsonElementExtensions.cs b/LibMatrix/Extensions/JsonElementExtensions.cs
index 99fa72d..f39f300 100644
--- a/LibMatrix/Extensions/JsonElementExtensions.cs
+++ b/LibMatrix/Extensions/JsonElementExtensions.cs
@@ -1,6 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Nodes;
diff --git a/LibMatrix/Filters/SyncFilter.cs b/LibMatrix/Filters/SyncFilter.cs
index e281346..c907f6b 100644
--- a/LibMatrix/Filters/SyncFilter.cs
+++ b/LibMatrix/Filters/SyncFilter.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
 
 namespace LibMatrix.Filters;
diff --git a/LibMatrix/Helpers/MatrixEventAttribute.cs b/LibMatrix/Helpers/MatrixEventAttribute.cs
index 7556019..7efc039 100644
--- a/LibMatrix/Helpers/MatrixEventAttribute.cs
+++ b/LibMatrix/Helpers/MatrixEventAttribute.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace LibMatrix.Helpers;
 
 [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
diff --git a/LibMatrix/Helpers/MediaResolver.cs b/LibMatrix/Helpers/MediaResolver.cs
index 6ddb221..5886618 100644
--- a/LibMatrix/Helpers/MediaResolver.cs
+++ b/LibMatrix/Helpers/MediaResolver.cs
@@ -1,6 +1,7 @@
+using LibMatrix.Services;
+
 namespace LibMatrix.Helpers;
 
-public class MediaResolver {
-    public static string ResolveMediaUri(string homeserver, string mxc) =>
-        mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/");
+public static class MediaResolver {
+    public static string ResolveMediaUri(string homeserver, string mxc) => mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/");
 }
diff --git a/LibMatrix/Helpers/MessageFormatter.cs b/LibMatrix/Helpers/MessageFormatter.cs
new file mode 100644
index 0000000..ff0a00f
--- /dev/null
+++ b/LibMatrix/Helpers/MessageFormatter.cs
@@ -0,0 +1,39 @@
+using ArcaneLibs.Extensions;
+using LibMatrix.StateEventTypes.Spec;
+
+namespace LibMatrix.Helpers;
+
+public static class MessageFormatter {
+    public static RoomMessageEventData FormatError(string error) {
+        return new RoomMessageEventData(body: error, messageType: "m.text") {
+            FormattedBody = $"<font color=\"#FF0000\">{error}: {error}</font>",
+            Format = "org.matrix.custom.html"
+        };
+    }
+
+    public static RoomMessageEventData FormatException(string error, Exception e) {
+        return new RoomMessageEventData(body: $"{error}: {e.Message}", messageType: "m.text") {
+            FormattedBody = $"<font color=\"#FF0000\">{error}: <pre>{e.Message}</pre>" +
+                            $"</font>",
+            Format = "org.matrix.custom.html"
+        };
+    }
+
+    public static RoomMessageEventData FormatSuccess(string text) {
+        return new RoomMessageEventData(body: text, messageType: "m.text") {
+            FormattedBody = $"<font color=\"#00FF00\">{text}</font>",
+            Format = "org.matrix.custom.html"
+        };
+    }
+
+    public static RoomMessageEventData FormatSuccessJson(string text, object data) {
+        return new RoomMessageEventData(body: text, messageType: "m.text") {
+            FormattedBody = $"<font color=\"#00FF00\">{text}: <pre>{data.ToJson(ignoreNull: true)}</pre></font>",
+            Format = "org.matrix.custom.html"
+        };
+    }
+
+    public static string HtmlFormatMention(string id, string? displayName = null) {
+        return $"<a href=\"https://matrix.to/#/{id}\">{displayName ?? id}</a>";
+    }
+}
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index 83b1685..d719184 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -1,13 +1,7 @@
-using System;
-using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
-using System.Linq;
 using System.Net.Http.Json;
 using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
 using ArcaneLibs.Extensions;
-using LibMatrix.Extensions;
 using LibMatrix.Filters;
 using LibMatrix.Homeservers;
 using LibMatrix.Responses;
@@ -15,15 +9,7 @@ using LibMatrix.Services;
 
 namespace LibMatrix.Helpers;
 
-public class SyncHelper {
-    private readonly AuthenticatedHomeserverGeneric _homeserver;
-    private readonly TieredStorageService _storageService;
-
-    public SyncHelper(AuthenticatedHomeserverGeneric homeserver, TieredStorageService storageService) {
-        _homeserver = homeserver;
-        _storageService = storageService;
-    }
-
+public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, TieredStorageService storageService) {
     public async Task<SyncResult?> Sync(
         string? since = null,
         int? timeout = 30000,
@@ -31,7 +17,7 @@ public class SyncHelper {
         SyncFilter? filter = null,
         CancellationToken? cancellationToken = null) {
         var outFileName = "sync-" +
-                          (await _storageService.CacheStorageProvider.GetAllKeysAsync()).Count(
+                          (await storageService.CacheStorageProvider.GetAllKeysAsync()).Count(
                               x => x.StartsWith("sync")) +
                           ".json";
         var url = $"/_matrix/client/v3/sync?timeout={timeout}&set_presence={setPresence}";
@@ -40,7 +26,7 @@ public class SyncHelper {
         // else url += "&full_state=true";
         Console.WriteLine("Calling: " + url);
         try {
-            var req = await _homeserver._httpClient.GetAsync(url, cancellationToken: cancellationToken ?? CancellationToken.None);
+            var req = await homeserver._httpClient.GetAsync(url, cancellationToken: cancellationToken ?? CancellationToken.None);
 
             // var res = await JsonSerializer.DeserializeAsync<SyncResult>(await req.Content.ReadAsStreamAsync());
 
@@ -79,10 +65,10 @@ public class SyncHelper {
         SyncFilter? filter = null,
         CancellationToken? cancellationToken = null
     ) {
-        await Task.WhenAll((await _storageService.CacheStorageProvider.GetAllKeysAsync())
+        await Task.WhenAll((await storageService.CacheStorageProvider.GetAllKeysAsync())
             .Where(x => x.StartsWith("sync"))
             .ToList()
-            .Select(x => _storageService.CacheStorageProvider.DeleteObjectAsync(x)));
+            .Select(x => storageService.CacheStorageProvider.DeleteObjectAsync(x)));
         var nextBatch = since;
         while (cancellationToken is null || !cancellationToken.Value.IsCancellationRequested) {
             var sync = await Sync(since: nextBatch, timeout: timeout, setPresence: setPresence, filter: filter,
@@ -116,7 +102,15 @@ public class SyncHelper {
                     if(updatedRoom.Value.Timeline is null) continue;
                     foreach (var stateEventResponse in updatedRoom.Value.Timeline.Events) {
                         stateEventResponse.RoomId = updatedRoom.Key;
-                        var tasks = TimelineEventHandlers.Select(x => x(stateEventResponse)).ToList();
+                        var tasks = TimelineEventHandlers.Select(x => {
+                            try {
+                                return x(stateEventResponse);
+                            }
+                            catch (Exception e) {
+                                Console.WriteLine(e);
+                                return Task.CompletedTask;
+                            }
+                        }).ToList();
                         await Task.WhenAll(tasks);
                     }
                 }
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index bb34112..0b3201c 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -1,13 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
 using System.Net.Http.Json;
 using System.Text.Json;
 using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
-using System.Threading.Tasks;
 using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Responses;
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
index 3b0bc10..218ded0 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
@@ -1,5 +1,3 @@
-using System;
-using System.Collections.Generic;
 using ArcaneLibs.Extensions;
 using LibMatrix.Filters;
 using LibMatrix.Responses.Admin;
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index fc31f4f..923986d 100644
--- a/LibMatrix/Homeservers/RemoteHomeServer.cs
+++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
 using System.Net.Http.Json;
-using System.Threading;
-using System.Threading.Tasks;
 using LibMatrix.Extensions;
 using LibMatrix.Responses;
 using LibMatrix.StateEventTypes.Spec;
diff --git a/LibMatrix/Interfaces/Services/IStorageProvider.cs b/LibMatrix/Interfaces/Services/IStorageProvider.cs
index e07e136..519d8ed 100644
--- a/LibMatrix/Interfaces/Services/IStorageProvider.cs
+++ b/LibMatrix/Interfaces/Services/IStorageProvider.cs
@@ -1,8 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
 namespace LibMatrix.Interfaces.Services;
 
 public interface IStorageProvider {
diff --git a/LibMatrix/LibMatrix.csproj b/LibMatrix/LibMatrix.csproj
index 8ae57cc..709e079 100644
--- a/LibMatrix/LibMatrix.csproj
+++ b/LibMatrix/LibMatrix.csproj
@@ -4,6 +4,7 @@
         <TargetFramework>net7.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
+        <LangVersion>preview</LangVersion>
     </PropertyGroup>
 
     <ItemGroup>
diff --git a/LibMatrix/MatrixException.cs b/LibMatrix/MatrixException.cs
index 1b38a6e..3aaad19 100644
--- a/LibMatrix/MatrixException.cs
+++ b/LibMatrix/MatrixException.cs
@@ -1,7 +1,5 @@
-using System;
 using System.Text.Json.Serialization;
 using ArcaneLibs.Extensions;
-using LibMatrix.Extensions;
 
 namespace LibMatrix;
 
diff --git a/LibMatrix/MessagesResponse.cs b/LibMatrix/MessagesResponse.cs
index d7bb54a..f09d136 100644
--- a/LibMatrix/MessagesResponse.cs
+++ b/LibMatrix/MessagesResponse.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
 using LibMatrix.Responses;
 
diff --git a/LibMatrix/Responses/Admin/AdminRoomListingResult.cs b/LibMatrix/Responses/Admin/AdminRoomListingResult.cs
index bbc23e6..f035184 100644
--- a/LibMatrix/Responses/Admin/AdminRoomListingResult.cs
+++ b/LibMatrix/Responses/Admin/AdminRoomListingResult.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
 
 namespace LibMatrix.Responses.Admin;
diff --git a/LibMatrix/Responses/ClientVersionsResponse.cs b/LibMatrix/Responses/ClientVersionsResponse.cs
index 7fac565..8e0a92a 100644
--- a/LibMatrix/Responses/ClientVersionsResponse.cs
+++ b/LibMatrix/Responses/ClientVersionsResponse.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
 
 namespace LibMatrix.Responses;
diff --git a/LibMatrix/Responses/CreateRoomRequest.cs b/LibMatrix/Responses/CreateRoomRequest.cs
index 2c05088..24c9ae0 100644
--- a/LibMatrix/Responses/CreateRoomRequest.cs
+++ b/LibMatrix/Responses/CreateRoomRequest.cs
@@ -1,11 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
 using System.Text.RegularExpressions;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Homeservers;
 using LibMatrix.StateEventTypes.Spec;
@@ -85,7 +81,7 @@ public class CreateRoomRequest {
     }
 
     public static CreateRoomRequest CreatePrivate(AuthenticatedHomeserverGeneric hs, string? name = null, string? roomAliasName = null) {
-        var request = new CreateRoomRequest() {
+        var request = new CreateRoomRequest {
             Name = name ?? "Private Room",
             Visibility = "private",
             CreationContent = new(),
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 8ba9a4b..4c784ce 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -1,16 +1,12 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net.Http;
 using System.Net.Http.Json;
 using System.Text.Json;
 using System.Text.Json.Serialization;
-using System.Threading.Tasks;
 using System.Web;
 using LibMatrix.Extensions;
 using LibMatrix.Homeservers;
 using LibMatrix.Responses;
 using LibMatrix.StateEventTypes.Spec;
+using Microsoft.Extensions.Logging;
 
 namespace LibMatrix.RoomTypes;
 
@@ -19,6 +15,8 @@ public class GenericRoom {
     internal readonly MatrixHttpClient _httpClient;
 
     public GenericRoom(AuthenticatedHomeserverGeneric homeserver, string roomId) {
+        if (string.IsNullOrWhiteSpace(roomId))
+            throw new ArgumentException("Room ID cannot be null or whitespace", nameof(roomId));
         Homeserver = homeserver;
         _httpClient = homeserver._httpClient;
         RoomId = roomId;
@@ -105,18 +103,24 @@ public class GenericRoom {
         });
     }
 
-
     // TODO: rewrite (members endpoint?)
     public async IAsyncEnumerable<StateEventResponse> GetMembersAsync(bool joinedOnly = true) {
-        var res = GetFullStateAsync();
-        await foreach (var member in res) {
-            if (member?.Type != "m.room.member") continue;
-            if (joinedOnly && (member.TypedContent as RoomMemberEventData)?.Membership is not "join") continue;
-            yield return member;
+        // var res = GetFullStateAsync();
+        // await foreach (var member in res) {
+        //     if (member?.Type != "m.room.member") continue;
+        //     if (joinedOnly && (member.TypedContent as RoomMemberEventData)?.Membership is not "join") continue;
+        //     yield return member;
+        // }
+        var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members");
+        var result =
+            JsonSerializer.DeserializeAsyncEnumerable<StateEventResponse>(await res.Content.ReadAsStreamAsync());
+        await foreach (var resp in result) {
+            if (resp?.Type != "m.room.member") continue;
+            if (joinedOnly && (resp.TypedContent as RoomMemberEventData)?.Membership is not "join") continue;
+            yield return resp;
         }
     }
 
-
 #region Utility shortcuts
 
     public async Task<List<string>> GetAliasesAsync() {
@@ -150,13 +154,11 @@ public class GenericRoom {
         return res.Type;
     }
 
-    public async Task<RoomPowerLevelEventData?> GetPowerLevelAsync() =>
+    public async Task<RoomPowerLevelEventData?> GetPowerLevelsAsync() =>
         await GetStateAsync<RoomPowerLevelEventData>("m.room.power_levels");
 
 #endregion
 
-
-
     public async Task ForgetAsync() =>
         await _httpClient.PostAsync($"/_matrix/client/v3/rooms/{RoomId}/forget", null);
 
@@ -178,12 +180,16 @@ public class GenericRoom {
             new UserIdAndReason { UserId = userId });
 
     public async Task<EventIdResponse> SendStateEventAsync(string eventType, object content) =>
-        await (await _httpClient.PostAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content))
+        await (await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}", content))
+            .Content.ReadFromJsonAsync<EventIdResponse>();
+
+    public async Task<EventIdResponse> SendStateEventAsync(string eventType, string stateKey, object content) =>
+        await (await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/rooms/{RoomId}/state/{eventType}/{stateKey}", content))
             .Content.ReadFromJsonAsync<EventIdResponse>();
 
     public async Task<EventIdResponse> SendMessageEventAsync(string eventType, RoomMessageEventData content) {
         var res = await _httpClient.PutAsJsonAsync(
-            $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content, new  JsonSerializerOptions() {
+            $"/_matrix/client/v3/rooms/{RoomId}/send/{eventType}/" + Guid.NewGuid(), content, new JsonSerializerOptions {
                 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
             });
         var resu = await res.Content.ReadFromJsonAsync<EventIdResponse>();
@@ -227,4 +233,10 @@ public class GenericRoom {
     public async Task<T> GetEvent<T>(string eventId) {
         return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/rooms/{RoomId}/event/{eventId}");
     }
+
+    public async Task<EventIdResponse> RedactEventAsync(string eventToRedact, string reason) {
+        var data = new { reason };
+        return (await (await _httpClient.PutAsJsonAsync(
+            $"/_matrix/client/v3/rooms/{RoomId}/redact/{eventToRedact}/{Guid.NewGuid()}", data)).Content.ReadFromJsonAsync<EventIdResponse>())!;
+    }
 }
diff --git a/LibMatrix/RoomTypes/SpaceRoom.cs b/LibMatrix/RoomTypes/SpaceRoom.cs
index 017a123..e1e9879 100644
--- a/LibMatrix/RoomTypes/SpaceRoom.cs
+++ b/LibMatrix/RoomTypes/SpaceRoom.cs
@@ -1,7 +1,4 @@
-using System.Collections.Generic;
-using System.Threading;
 using ArcaneLibs.Extensions;
-using LibMatrix.Extensions;
 using LibMatrix.Homeservers;
 
 namespace LibMatrix.RoomTypes;
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index 776c7eb..71d9860 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -1,11 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Net.Http.Headers;
 using System.Net.Http.Json;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
 using ArcaneLibs.Extensions;
 using LibMatrix.Extensions;
 using LibMatrix.Homeservers;
diff --git a/LibMatrix/Services/HomeserverResolverService.cs b/LibMatrix/Services/HomeserverResolverService.cs
index dcd0fe9..f2c0781 100644
--- a/LibMatrix/Services/HomeserverResolverService.cs
+++ b/LibMatrix/Services/HomeserverResolverService.cs
@@ -1,26 +1,16 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
 using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
 using ArcaneLibs.Extensions;
 using LibMatrix.Extensions;
 using Microsoft.Extensions.Logging;
 
 namespace LibMatrix.Services;
 
-public class HomeserverResolverService {
+public class HomeserverResolverService(ILogger<HomeserverResolverService>? logger) {
     private readonly MatrixHttpClient _httpClient = new();
-    private readonly ILogger<HomeserverResolverService> _logger;
 
     private static readonly Dictionary<string, string> _wellKnownCache = new();
     private static readonly Dictionary<string, SemaphoreSlim> _wellKnownSemaphores = new();
 
-    public HomeserverResolverService(ILogger<HomeserverResolverService> logger) {
-        _logger = logger;
-    }
-
     public async Task<string> ResolveHomeserverFromWellKnown(string homeserver) {
         var res = await _resolveHomeserverFromWellKnown(homeserver);
         if (!res.StartsWith("http")) res = "https://" + res;
@@ -38,7 +28,7 @@ public class HomeserverResolverService {
         }
 
         string? result = null;
-        _logger.LogInformation("Attempting to resolve homeserver: {}", homeserver);
+        logger?.LogInformation("Attempting to resolve homeserver: {}", homeserver);
         result ??= await _tryResolveFromClientWellknown(homeserver);
         result ??= await _tryResolveFromServerWellknown(homeserver);
         result ??= await _tryCheckIfDomainHasHomeserver(homeserver);
@@ -46,7 +36,7 @@ public class HomeserverResolverService {
         if (result is null) throw new InvalidDataException($"Failed to resolve homeserver for {homeserver}! Is it online and configured correctly?");
 
         //success!
-        _logger.LogInformation("Resolved homeserver: {} -> {}", homeserver, result);
+        logger?.LogInformation("Resolved homeserver: {} -> {}", homeserver, result);
         _wellKnownCache[homeserver] = result;
         sem.Release();
         return result;
@@ -60,7 +50,7 @@ public class HomeserverResolverService {
             return hs;
         }
 
-        _logger.LogInformation("No client well-known...");
+        logger?.LogInformation("No client well-known...");
         return null;
     }
 
@@ -72,15 +62,15 @@ public class HomeserverResolverService {
             return hs;
         }
 
-        _logger.LogInformation("No server well-known...");
+        logger?.LogInformation("No server well-known...");
         return null;
     }
 
     private async Task<string?> _tryCheckIfDomainHasHomeserver(string homeserver) {
-        _logger.LogInformation("Checking if {} hosts a homeserver...", homeserver);
+        logger?.LogInformation("Checking if {} hosts a homeserver...", homeserver);
         if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions"))
             return homeserver;
-        _logger.LogInformation("No homeserver on shortname...");
+        logger?.LogInformation("No homeserver on shortname...");
         return null;
     }
 
@@ -88,4 +78,12 @@ public class HomeserverResolverService {
         homeserver = homeserver.Replace("https://", $"https://{subdomain}.");
         return await _tryCheckIfDomainHasHomeserver(homeserver);
     }
+
+    public async Task<string?> ResolveMediaUri(string homeserver, string mxc) {
+        if (homeserver is null) throw new ArgumentNullException(nameof(homeserver));
+        if (mxc is null) throw new ArgumentNullException(nameof(mxc));
+        if (!mxc.StartsWith("mxc://")) throw new InvalidDataException("mxc must start with mxc://");
+        homeserver = await ResolveHomeserverFromWellKnown(homeserver);
+        return mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/");
+    }
 }
diff --git a/LibMatrix/Services/ServiceInstaller.cs b/LibMatrix/Services/ServiceInstaller.cs
index 9c4cdb9..b1c98e1 100644
--- a/LibMatrix/Services/ServiceInstaller.cs
+++ b/LibMatrix/Services/ServiceInstaller.cs
@@ -1,5 +1,3 @@
-using System;
-using System.Linq;
 using Microsoft.Extensions.DependencyInjection;
 
 namespace LibMatrix.Services;
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index 26dc39a..175d706 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -1,13 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
 using ArcaneLibs;
 using ArcaneLibs.Extensions;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs b/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs
index c3fee34..7a4b3f3 100644
--- a/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs
+++ b/LibMatrix/StateEventTypes/Common/MjolnirShortcodeEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs b/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs
index 754a9dc..ad65b0f 100644
--- a/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs
+++ b/LibMatrix/StateEventTypes/Common/RoomEmotesEventData.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventData.cs b/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventData.cs
index 384ca43..269bd6d 100644
--- a/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/CanonicalAliasEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs b/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs
index eadba67..7ba3428 100644
--- a/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/GuestAccessEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs b/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs
index 1c73346..deca7c8 100644
--- a/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/HistoryVisibilityEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs b/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
index 08e8f22..b64c1dd 100644
--- a/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/JoinRulesEventData.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs b/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs
index c0aed9e..2e66bd9 100644
--- a/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/PolicyRuleStateEventData.cs
@@ -1,6 +1,4 @@
-using System;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs b/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs
index c5a95ae..5167502 100644
--- a/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/PresenceStateEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs
index df80a08..3f2c39e 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomAliasEventData.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs
index 4d3fabf..f71e1fb 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomAvatarEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs
index 0b1bd5c..31f9411 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomCreateEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs
index 126117d..9673dcc 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomEncryptionEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs
index 7c181ae..c99aa8d 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomMemberEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
index 11a0e82..d13c273 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomMessageEventData.cs
@@ -1,5 +1,5 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
+using ArcaneLibs.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
@@ -7,8 +7,21 @@ namespace LibMatrix.StateEventTypes.Spec;
 
 [MatrixEvent(EventName = "m.room.message")]
 public class RoomMessageEventData : IStateEventType {
+    public RoomMessageEventData() { }
+
+    public RoomMessageEventData(string messageType, string body) {
+        MessageType = messageType;
+        Body = body;
+    }
+
+    public RoomMessageEventData(string body) : this() {
+        Body = body;
+        MessageType = "m.notice";
+    }
+
     [JsonPropertyName("body")]
     public string Body { get; set; }
+
     [JsonPropertyName("msgtype")]
     public string MessageType { get; set; } = "m.notice";
 
@@ -28,7 +41,6 @@ public class RoomMessageEventData : IStateEventType {
     public string? Url { get; set; }
 
     public class MessageRelatesTo {
-
         [JsonPropertyName("m.in_reply_to")]
         public MessageInReplyTo? InReplyTo { get; set; }
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs
index 2245793..e04f0dc 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomNameEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs
index 10ef3f5..bb78aeb 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomPinnedEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs
index 3c985f6..b4f7d53 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomPowerLevelEventData.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs
index eaf9e8c..c3deb98 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomTopicEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs b/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs
index cebb238..3812c46 100644
--- a/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/RoomTypingEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs b/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs
index a258707..d00b464 100644
--- a/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/ServerACLEventData.cs
@@ -1,6 +1,4 @@
-using System.Collections.Generic;
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs b/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs
index 5ccab88..e8c6d18 100644
--- a/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/SpaceChildEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;
 
diff --git a/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs b/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs
index 6477290..ebd083e 100644
--- a/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs
+++ b/LibMatrix/StateEventTypes/Spec/SpaceParentEventData.cs
@@ -1,5 +1,4 @@
 using System.Text.Json.Serialization;
-using LibMatrix.Extensions;
 using LibMatrix.Helpers;
 using LibMatrix.Interfaces;