about summary refs log tree commit diff
path: root/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-05-07 20:58:12 +0200
committerRory& <root@rory.gay>2025-05-07 20:58:12 +0200
commita0fc4d145c77cc14af09cbbe285a0835e842728a (patch)
tree6958d610b44d11f90ba31059eb35ae8447828054 /LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs
parentSync preprocessor support (diff)
downloadLibMatrix-a0fc4d145c77cc14af09cbbe285a0835e842728a.tar.xz
MSC4222 emulation for left rooms
Diffstat (limited to 'LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs')
-rw-r--r--LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs125
1 files changed, 125 insertions, 0 deletions
diff --git a/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs b/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs
new file mode 100644

index 0000000..b18a8e0 --- /dev/null +++ b/LibMatrix/Helpers/SyncProcessors/Msc4222EmulationSyncProcessor.cs
@@ -0,0 +1,125 @@ +using System.Diagnostics; +using ArcaneLibs.Extensions; +using LibMatrix.Homeservers; +using LibMatrix.Responses; + +namespace LibMatrix.Helpers.SyncProcessors; + +public class Msc4222EmulationSyncProcessor(AuthenticatedHomeserverGeneric homeserver) { + private static bool StateEventsMatch(StateEventResponse a, StateEventResponse b) { + return a.Type == b.Type && a.StateKey == b.StateKey && a.OriginServerTs < b.OriginServerTs; + } + + public async Task<SyncResponse?> EmulateMsc4222(SyncResponse? resp) { + var sw = Stopwatch.StartNew(); + if (resp is null or { Rooms: null }) return resp; + + if ( + resp.Rooms.Join?.Any(x => x.Value.StateAfter is { Events.Count: > 0 }) == true + || resp.Rooms.Leave?.Any(x => x.Value.StateAfter is { Events.Count: > 0 }) == true + ) { + Console.WriteLine($"Msc4222EmulationSyncProcessor.EmulateMsc4222 determined that no emulation is needed in {sw.Elapsed}"); + return resp; + } + + var modified = false; + List<Task<bool>> tasks = []; + if (resp.Rooms is { Join.Count: > 0 }) { + tasks.AddRange(resp.Rooms.Join.Select(ProcessJoinedRooms)); + } + + if (resp.Rooms is { Leave.Count: > 0 }) { + tasks.AddRange(resp.Rooms.Leave.Select(ProcessLeftRooms)); + } + + var tasksEnum = tasks.ToAsyncEnumerable(); + await foreach (var wasModified in tasksEnum) { + if (wasModified) { + modified = true; + } + } + + Console.WriteLine($"Msc4222EmulationSyncProcessor.EmulateMsc4222 processed {resp.Rooms?.Join?.Count}/{resp.Rooms?.Leave?.Count} rooms in {sw.Elapsed}"); + if (modified) + resp.Msc4222Method = SyncResponse.Msc4222SyncType.Emulated; + + return resp; + } + + private async Task<bool> ProcessJoinedRooms(KeyValuePair<string, SyncResponse.RoomsDataStructure.JoinedRoomDataStructure> roomData) { + var (roomId, data) = roomData; + + if (data.StateAfter is { Events.Count: > 0 }) { + return false; + } + + data.StateAfter = new() { }; + + return false; + } + + private async Task<bool> ProcessLeftRooms(KeyValuePair<string, SyncResponse.RoomsDataStructure.LeftRoomDataStructure> roomData) { + var (roomId, data) = roomData; + var room = homeserver.GetRoom(roomId); + + if (data.StateAfter is { Events.Count: > 0 }) { + return false; + } + + data.StateAfter = new() { + Events = [] + }; + + try { + data.StateAfter.Events = await room.GetFullStateAsListAsync(); + return true; + } + catch (Exception e) { + Console.WriteLine($"Msc4222Emulation: Failed to get full state for room {roomId}, state may be incomplete!\n{e}"); + } + + var oldState = new List<StateEventResponse>(); + if (data.State is { Events.Count: > 0 }) { + oldState.ReplaceBy(data.State.Events, StateEventsMatch); + } + + if (data.Timeline is { Limited: true }) { + if (data.Timeline.Events != null) + oldState.ReplaceBy(data.Timeline.Events, StateEventsMatch); + + try { + var timeline = await homeserver.GetRoom(roomId).GetMessagesAsync(limit: 250); + if (timeline is { State.Count: > 0 }) { + oldState.ReplaceBy(timeline.State, StateEventsMatch); + } + + if (timeline is { Chunk.Count: > 0 }) { + oldState.ReplaceBy(timeline.Chunk.Where(x => x.StateKey != null), StateEventsMatch); + } + } + catch (Exception e) { + Console.WriteLine($"Msc4222Emulation: Failed to get timeline for room {roomId}, state may be incomplete!\n{e}"); + } + } + + oldState = oldState.DistinctBy(x => (x.Type, x.StateKey)).ToList(); + + var tasks = oldState + .Select(async oldEvt => { + try { + return await room.GetStateEventAsync(oldEvt.Type, oldEvt.StateKey!); + } + catch (Exception e) { + Console.WriteLine($"Msc4222Emulation: Failed to get state event {oldEvt.Type}/{oldEvt.StateKey} for room {roomId}, state may be incomplete!\n{e}"); + return oldEvt; + } + }); + + var tasksEnum = tasks.ToAsyncEnumerable(); + await foreach (var evt in tasksEnum) { + data.StateAfter.Events.Add(evt); + } + + return true; + } +} \ No newline at end of file