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;
}
|