about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ExampleBots/MediaModeratorPoC/Commands/BanMediaCommand.cs3
-rw-r--r--ExampleBots/MediaModeratorPoC/StateEventTypes/BasePolicy.cs3
-rw-r--r--LibMatrix/Extensions/HttpClientExtensions.cs3
-rw-r--r--LibMatrix/Helpers/SyncHelper.cs9
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs129
-rw-r--r--LibMatrix/Services/HomeserverProviderService.cs2
6 files changed, 141 insertions, 8 deletions
diff --git a/ExampleBots/MediaModeratorPoC/Commands/BanMediaCommand.cs b/ExampleBots/MediaModeratorPoC/Commands/BanMediaCommand.cs
index 69c0583..5dfa706 100644
--- a/ExampleBots/MediaModeratorPoC/Commands/BanMediaCommand.cs
+++ b/ExampleBots/MediaModeratorPoC/Commands/BanMediaCommand.cs
@@ -1,3 +1,4 @@
+using System.Buffers.Text;
 using System.Security.Cryptography;
 using ArcaneLibs.Extensions;
 using LibMatrix;
@@ -87,7 +88,7 @@ public class BanMediaCommand(IServiceProvider services, HomeserverProviderServic
 
                 MediaPolicyEventContent policy;
                 await policyRoom.SendStateEventAsync("gay.rory.media_moderator_poc.rule.media", Guid.NewGuid().ToString(), policy = new MediaPolicyEventContent {
-                    Entity = uriHash,
+                    // Entity = uriHash,
                     FileHash = fileHash,
                     Reason = string.Join(' ', ctx.Args[1..]),
                     Recommendation = recommendation,
diff --git a/ExampleBots/MediaModeratorPoC/StateEventTypes/BasePolicy.cs b/ExampleBots/MediaModeratorPoC/StateEventTypes/BasePolicy.cs
index 048c1d0..7735314 100644
--- a/ExampleBots/MediaModeratorPoC/StateEventTypes/BasePolicy.cs
+++ b/ExampleBots/MediaModeratorPoC/StateEventTypes/BasePolicy.cs
@@ -1,10 +1,11 @@
 using System.ComponentModel.DataAnnotations;
 using System.Text.Json.Serialization;
 using LibMatrix;
+using LibMatrix.Interfaces;
 
 namespace MediaModeratorPoC.StateEventTypes;
 
-public abstract class BasePolicy : StateEvent {
+public abstract class BasePolicy : EventContent {
     /// <summary>
     ///     Entity this policy applies to
     /// </summary>
diff --git a/LibMatrix/Extensions/HttpClientExtensions.cs b/LibMatrix/Extensions/HttpClientExtensions.cs
index bffd74a..d280ef3 100644
--- a/LibMatrix/Extensions/HttpClientExtensions.cs
+++ b/LibMatrix/Extensions/HttpClientExtensions.cs
@@ -111,6 +111,9 @@ public class MatrixHttpClient : HttpClient {
         options = GetJsonSerializerOptions(options);
         var request = new HttpRequestMessage(HttpMethod.Put, requestUri);
         request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+        Console.WriteLine($"Sending PUT {requestUri}");
+        Console.WriteLine($"Content: {value.ToJson()}");
+        Console.WriteLine($"Type: {value.GetType().FullName}");
         request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options),
             Encoding.UTF8, "application/json");
         return await SendAsync(request, cancellationToken);
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index 6bdf5ad..bfcd650 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -14,6 +14,8 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
     public SyncFilter? Filter { get; set; }
     public bool FullState { get; set; } = false;
 
+    public bool IsInitialSync { get; set; } = true;
+
     public async Task<SyncResponse?> SyncAsync(CancellationToken? cancellationToken = null) {
         var url = $"/_matrix/client/v3/sync?timeout={Timeout}&set_presence={SetPresence}&full_state={(FullState ? "true" : "false")}";
         if (!string.IsNullOrWhiteSpace(Since)) url += $"&since={Since}";
@@ -39,14 +41,13 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
         while (!cancellationToken?.IsCancellationRequested ?? true) {
             var sync = await SyncAsync(cancellationToken);
             if (sync is null) continue;
-            Since = sync.NextBatch ?? Since;
+            Since = string.IsNullOrWhiteSpace(sync?.NextBatch) ? Since : sync.NextBatch;
             yield return sync;
         }
     }
 
     public async Task RunSyncLoopAsync(bool skipInitialSyncEvents = true, CancellationToken? cancellationToken = null) {
         var sw = Stopwatch.StartNew();
-        bool isInitialSync = true;
         int emptyInitialSyncCount = 0;
         var oldTimeout = Timeout;
         Timeout = 0;
@@ -55,12 +56,12 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
             if (sync?.ToJson(ignoreNull: true, indent: false).Length < 250) {
                 emptyInitialSyncCount++;
                 if (emptyInitialSyncCount > 5) {
-                    isInitialSync = false;
+                    IsInitialSync = false;
                     Timeout = oldTimeout;
                 }
             }
 
-            await RunSyncLoopCallbacksAsync(sync, isInitialSync && skipInitialSyncEvents);
+            await RunSyncLoopCallbacksAsync(sync, IsInitialSync && skipInitialSyncEvents);
         }
     }
 
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index 8a6e114..bc4ea1a 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -5,6 +5,8 @@ using System.Text.Json.Nodes;
 using System.Text.Json.Serialization;
 using ArcaneLibs.Extensions;
 using LibMatrix.EventTypes.Spec.State;
+using LibMatrix.Filters;
+using LibMatrix.Helpers;
 using LibMatrix.Responses;
 using LibMatrix.RoomTypes;
 using LibMatrix.Services;
@@ -163,4 +165,129 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
     }
 
 #endregion
-}
+
+    public string? ResolveMediaUri(string? mxcUri) {
+        if (mxcUri is null) return null;
+        return $"{_httpClient.BaseAddress}/_matrix/media/v3/download/{mxcUri.Replace("mxc://", "")}".Replace("//", "/");
+    }
+
+    public async Task UpdateProfileAsync(ProfileResponseEventContent? newProfile, bool preserveCustomRoomProfile = true) {
+        Console.WriteLine($"Updating profile for {WhoAmI.UserId} to {newProfile.ToJson(ignoreNull: true)} (preserving room profiles: {preserveCustomRoomProfile})");
+        if (newProfile is null) return;
+        var oldProfile = await GetProfileAsync(WhoAmI.UserId!);
+        Dictionary<string, RoomMemberEventContent> targetRoomProfileOverrides = new();
+        var syncHelper = new SyncHelper(this) {
+            Filter = new SyncFilter {
+                AccountData = new SyncFilter.EventFilter() {
+                    Types = new List<string> {
+                        "m.room.member"
+                    }
+                }
+            },
+            Timeout = 250
+        };
+        int targetSyncCount = 0;
+
+        if (preserveCustomRoomProfile) {
+            var rooms = await GetJoinedRooms();
+            targetSyncCount = rooms.Count;
+            foreach (var room in rooms) {
+                try {
+                    var currentRoomProfile = await room.GetStateAsync<RoomMemberEventContent>("m.room.member", WhoAmI.UserId!);
+                    //build new profiles
+
+                    if (currentRoomProfile.DisplayName == oldProfile.DisplayName) {
+                        currentRoomProfile.DisplayName = newProfile.DisplayName;
+                    }
+
+                    if (currentRoomProfile.AvatarUrl == oldProfile.AvatarUrl) {
+                        currentRoomProfile.AvatarUrl = newProfile.AvatarUrl;
+                    }
+
+                    targetRoomProfileOverrides.Add(room.RoomId, currentRoomProfile);
+                }
+                catch (Exception e) { }
+            }
+
+            Console.WriteLine($"Rooms with custom profiles: {string.Join(',', targetRoomProfileOverrides.Keys)}");
+        }
+
+        if (oldProfile.DisplayName != newProfile.DisplayName) {
+            await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/displayname", new { displayname = newProfile.DisplayName });
+        }
+        else {
+            Console.WriteLine($"Not updating display name because {oldProfile.DisplayName} == {newProfile.DisplayName}");
+        }
+
+        if (oldProfile.AvatarUrl != newProfile.AvatarUrl) {
+            await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/avatar_url", new { avatar_url = newProfile.AvatarUrl });
+        }
+        else {
+            Console.WriteLine($"Not updating avatar URL because {newProfile.AvatarUrl} == {newProfile.AvatarUrl}");
+        }
+
+        if (!preserveCustomRoomProfile) return;
+
+        int syncCount = 0;
+        await foreach (var sync in syncHelper.EnumerateSyncAsync()) {
+            if (sync.Rooms is null) break;
+            foreach (var (roomId, roomData) in sync.Rooms.Join) {
+                if (roomData.State is { Events: { Count: > 0 } }) {
+                    var updatedRoomProfile =
+                        roomData.State?.Events?.FirstOrDefault(x => x.Type == "m.room.member" && x.StateKey == WhoAmI.UserId)?.TypedContent as RoomMemberEventContent;
+                    if (updatedRoomProfile is null) continue;
+                    if (!targetRoomProfileOverrides.ContainsKey(roomId)) continue;
+                    var targetRoomProfileOverride = targetRoomProfileOverrides[roomId];
+                    var room = GetRoom(roomId);
+                    if (updatedRoomProfile.DisplayName != targetRoomProfileOverride.DisplayName || updatedRoomProfile.AvatarUrl != targetRoomProfileOverride.AvatarUrl)
+                        await room.SendStateEventAsync("m.room.member", WhoAmI.UserId, targetRoomProfileOverride);
+                }
+            }
+
+            var differenceFound = false;
+            if (syncCount++ >= targetSyncCount) {
+                var profiles = GetRoomProfilesAsync();
+                await foreach ((string roomId, var profile) in profiles) {
+                    if (!targetRoomProfileOverrides.ContainsKey(roomId)) continue;
+                    var targetRoomProfileOverride = targetRoomProfileOverrides[roomId];
+                    if (profile.DisplayName != targetRoomProfileOverride.DisplayName || profile.AvatarUrl != targetRoomProfileOverride.AvatarUrl) {
+                        differenceFound = true;
+                        break;
+                    }
+                }
+                // var rooms = await GetJoinedRooms();
+                // List<ProfileResponseEventContent> currentProfiles = new();
+                // foreach (var room in rooms) {
+                //     try {
+                //         var roomProfile = await room.GetStateAsync<RoomMemberEventContent>("m.room.member", WhoAmI.UserId!);
+                //         currentProfiles.Add(new ProfileResponseEventContent {
+                //             AvatarUrl = roomProfile.AvatarUrl,
+                //             DisplayName = roomProfile.DisplayName
+                //         });
+                //     }
+                //     catch (Exception e) { }
+                // }
+                // if (currentProfiles.All(x => x.DisplayName == newProfile.DisplayName) && currentProfiles.All(x => x.AvatarUrl == newProfile.AvatarUrl)) {
+                //     Console.WriteLine("All rooms have been updated");
+                //     break;
+                // }
+            }
+
+            if (!differenceFound) return;
+        }
+    }
+
+    public async IAsyncEnumerable<KeyValuePair<string, RoomMemberEventContent>> GetRoomProfilesAsync() {
+        var rooms = await GetJoinedRooms();
+        foreach (var room in rooms) {
+            RoomMemberEventContent? content = null;
+            try {
+                content = await room.GetStateAsync<RoomMemberEventContent>("m.room.member", WhoAmI.UserId!);
+            }
+            catch (Exception e) { }
+
+            if (content is not null)
+                yield return new KeyValuePair<string, RoomMemberEventContent>(room.RoomId, content!);
+        }
+    }
+}
\ No newline at end of file
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index 6659081..c7fa5c3 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -54,7 +54,7 @@ public class HomeserverProviderService {
     public async Task<RemoteHomeServer> GetRemoteHomeserver(string homeserver, string? proxy = null) {
         var hs = await RemoteHomeServer.Create(proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver));
         // hs._httpClient.Dispose();
-        // hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.FullHomeServerDomain) };
+        // hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.ServerName) };
         // hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
         return hs;
     }