diff --git a/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/MainTabComponents/MainTabSpaceItem.razor b/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/MainTabComponents/MainTabSpaceItem.razor
index 6483f01..596d63d 100644
--- a/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/MainTabComponents/MainTabSpaceItem.razor
+++ b/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/MainTabComponents/MainTabSpaceItem.razor
@@ -8,7 +8,7 @@
<span onclick="@ToggleSpace">▶ </span>
}
- <MxcImage Circular="true" Height="32" Width="32" Homeserver="Space.Room.Homeserver" MxcUri="@Space.RoomIcon"></MxcImage>
+ <MxcImage Circular="true" Height="32" Width="32" MxcUri="@Space.RoomIcon"></MxcImage>
<span class="spaceNameEllipsis">@Space.RoomName</span>
</div>
@if (IsSpaceOpened()) {
diff --git a/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/RoomsIndex2MainTab.razor b/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/RoomsIndex2MainTab.razor
index 7ccfae2..d2b6d5a 100644
--- a/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/RoomsIndex2MainTab.razor
+++ b/MatrixUtils.Web/Pages/Labs/Rooms2/Index2Components/RoomsIndex2MainTab.razor
@@ -22,26 +22,27 @@
@* </div> *@
@* </div> *@
-<div>
- <div class="row">
- <div class="col-3" style="background-color: #ffffff22;">
- <LinkButton>Uncategorised rooms</LinkButton>
- @foreach (var space in GetTopLevelSpaces()) {
- @* @RecursingSpaceChildren(space) *@
- <MainTabSpaceItem Space="space" OpenedSpaces="OpenedSpaces" @bind-SelectedSpace="SelectedSpace" />
- }
- </div>
- <div class="col-9" style="background-color: #ff00ff66;">
- <p>Placeholder for rooms list...</p>
- @if (SelectedSpace != null) {
- foreach (var room in GetSpaceChildRooms(SelectedSpace)) {
- <p>@room.RoomName</p>
+<CascadingValue Name="Homeserver" Value="@Data.Homeserver">
+ <div>
+ <div class="row">
+ <div class="col-3" style="background-color: #ffffff22;">
+ <LinkButton>Uncategorised rooms</LinkButton>
+ @foreach (var space in GetTopLevelSpaces()) {
+ @* @RecursingSpaceChildren(space) *@
+ <MainTabSpaceItem Space="space" OpenedSpaces="OpenedSpaces" @bind-SelectedSpace="SelectedSpace"/>
+ }
+ </div>
+ <div class="col-9" style="background-color: #ff00ff66;">
+ <p>Placeholder for rooms list...</p>
+ @if (SelectedSpace != null) {
+ foreach (var room in GetSpaceChildRooms(SelectedSpace)) {
+ <p>@room.RoomName</p>
+ }
}
- }
+ </div>
</div>
</div>
-</div>
-
+</CascadingValue>
@code {
@@ -118,7 +119,7 @@
var childSpaces = children.Where(x => x.RoomType == "m.space").ToList();
return childSpaces;
}
-
+
private List<RoomInfo> GetSpaceChildRooms(RoomInfo space) {
var children = GetSpaceChildren(space);
var childRooms = children.Where(x => x.RoomType != "m.space").ToList();
diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
index d57aa43..b2ce9c3 100644
--- a/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
+++ b/MatrixUtils.Web/Pages/Rooms/PolicyList.razor
@@ -6,13 +6,25 @@
@using System.Diagnostics
@using LibMatrix.RoomTypes
@using System.Collections.Frozen
+@using System.Numerics
@using System.Reflection
+@using System.Runtime.InteropServices.JavaScript
@using ArcaneLibs.Attributes
@using LibMatrix.EventTypes
+@using LibMatrix.EventTypes.Common
+@using LibMatrix.EventTypes.Interop.Draupnir
@using MatrixUtils.Web.Shared.PolicyEditorComponents
+@using SpawnDev.BlazorJS.WebWorkers
+@inject WebWorkerService WebWorkerService
-<h3>Policy list editor - Editing @RoomId</h3>
+<h3>Policy list editor - Editing @(RoomName ?? RoomId)</h3>
+@if (!string.IsNullOrWhiteSpace(DraupnirShortcode)) {
+ <span style="margin-right: 2em;">Shortcode: @DraupnirShortcode</span>
+}
+@if (!string.IsNullOrWhiteSpace(RoomAlias)) {
+ <span>Alias: @RoomAlias</span>
+}
<hr/>
@* <InputCheckbox @bind-Value="EnableAvatars"></InputCheckbox><label>Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!)</label> *@
<LinkButton OnClick="@(() => { CurrentlyEditingEvent = new() { Type = "", RawContent = new() }; return Task.CompletedTask; })">Create new policy</LinkButton>
@@ -56,10 +68,10 @@ else {
.Where(x => x.GetCustomAttribute<TableHideAttribute>() is null)
.ToFrozenSet();
var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet();
-
+
var proxySafeProps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => props.Any(y => y.Name == x.Name))
- .ToFrozenSet();
+ .ToFrozenSet();
Console.WriteLine($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}");
}
<thead>
@@ -87,6 +99,16 @@ else {
@if (policy.IsLegacyType) {
<LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Update policy type</LinkButton>
}
+
+ @if (PolicyTypeIds[typeof(ServerPolicyRuleEventContent)].Contains(policy.Type)) {
+ <LinkButton OnClick="@(() => { ServerPolicyToMakePermanent = policy; return Task.CompletedTask; })">Make permanent</LinkButton>
+ @if (CurrentUserIsDraupnir) {
+ <LinkButton Color="@(ActiveKicks.ContainsKey(policy) ? "#FF0000" : null)" OnClick="@(() => DraupnirKickMatching(policy))">Kick users @(ActiveKicks.ContainsKey(policy) ? $"({ActiveKicks[policy]})" : null)</LinkButton>
+ }
+ }
+ }
+ else {
+ <p>No permission to modify</p>
}
</div>
</td>
@@ -130,6 +152,12 @@ else {
<PolicyEditorModal PolicyEvent="@CurrentlyEditingEvent" OnClose="@(() => CurrentlyEditingEvent = null)" OnSave="@(e => UpdatePolicyAsync(e))"></PolicyEditorModal>
}
+@if (ServerPolicyToMakePermanent is not null) {
+ <ModalWindow Title="Make policy permanent">
+
+ </ModalWindow>
+}
+
@if (MassCreatePolicies) {
<MassPolicyEditorModal Room="@Room" OnClose="@(() => MassCreatePolicies = false)" OnSaved="@(() => { MassCreatePolicies = false; LoadStatesAsync(); })"></MassPolicyEditorModal>
}
@@ -150,6 +178,7 @@ else {
private bool _enableAvatars;
private StateEventResponse? _currentlyEditingEvent;
private bool _massCreatePolicies;
+ private StateEventResponse? _serverPolicyToMakePermanent;
private Dictionary<Type, List<StateEventResponse>> PolicyEventsByType { get; set; } = new();
@@ -161,9 +190,22 @@ else {
}
}
+ public StateEventResponse? ServerPolicyToMakePermanent {
+ get => _serverPolicyToMakePermanent;
+ set {
+ _serverPolicyToMakePermanent = value;
+ StateHasChanged();
+ }
+ }
+
private AuthenticatedHomeserverGeneric Homeserver { get; set; }
private GenericRoom Room { get; set; }
private RoomPowerLevelEventContent PowerLevels { get; set; }
+ public bool CurrentUserIsDraupnir { get; set; }
+ public string? RoomName { get; set; }
+ public string? RoomAlias { get; set; }
+ public string? DraupnirShortcode { get; set; }
+ public Dictionary<StateEventResponse, int> ActiveKicks { get; set; } = [];
public bool MassCreatePolicies {
get => _massCreatePolicies;
@@ -179,7 +221,13 @@ else {
Homeserver = (await RMUStorage.GetCurrentSessionOrNavigate())!;
if (Homeserver is null) return;
Room = Homeserver.GetRoom(RoomId!);
- PowerLevels = (await Room.GetPowerLevelsAsync())!;
+ await Task.WhenAll([
+ Task.Run(async () => { PowerLevels = (await Room.GetPowerLevelsAsync())!; }),
+ Task.Run(async () => { DraupnirShortcode = (await Room.GetStateOrNullAsync<MjolnirShortcodeEventContent>(MjolnirShortcodeEventContent.EventId))?.Shortcode; }),
+ Task.Run(async () => { RoomAlias = (await Room.GetCanonicalAliasAsync())?.Alias; }),
+ Task.Run(async () => { RoomName = await Room.GetNameOrFallbackAsync(); }),
+ Task.Run(async () => { CurrentUserIsDraupnir = (await Homeserver.GetAccountDataOrNullAsync<object>("org.matrix.mjolnir.protected_rooms")) is not null; }),
+ ]);
await LoadStatesAsync();
Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
}
@@ -236,4 +284,139 @@ else {
private static Dictionary<string, Type> PolicyTypes = KnownPolicyTypes
.ToDictionary(x => x.GetCustomAttributes<MatrixEventAttribute>().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x);
+ private static Dictionary<Type, string[]> PolicyTypeIds = KnownPolicyTypes
+ .ToDictionary(x => x, x => x.GetCustomAttributes<MatrixEventAttribute>().Select(y => y.EventName).ToArray());
+
+#region Draupnir interop
+
+ private SemaphoreSlim ss = new(16, 16);
+
+ private async Task DraupnirKickMatching(StateEventResponse policy) {
+ try {
+ var content = policy.TypedContent! as PolicyRuleEventContent;
+ if (content is null) return;
+ if (string.IsNullOrWhiteSpace(content.Entity)) return;
+
+ var data = await Homeserver.GetAccountDataAsync<DraupnirProtectedRoomsData>(DraupnirProtectedRoomsData.EventId);
+ var rooms = data.Rooms.Select(Homeserver.GetRoom).ToList();
+
+ ActiveKicks.Add(policy, rooms.Count);
+ StateHasChanged();
+ await Task.Delay(500);
+
+ // for (int i = 0; i < 12; i++) {
+ // _ = WebWorkerService.TaskPool.Invoke(WasteCpu);
+ // }
+
+ // static async Task runKicks(string roomId, PolicyRuleEventContent content) {
+ // Console.WriteLine($"Checking {roomId}...");
+ // // Console.WriteLine($"Checking {room.RoomId}...");
+ // //
+ // // try {
+ // // var members = await room.GetMembersListAsync();
+ // // foreach (var member in members) {
+ // // var membership = member.ContentAs<RoomMemberEventContent>();
+ // // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue;
+ // // if (membership?.Membership is "leave" or "ban") continue;
+ // //
+ // // if (content.EntityMatches(member.StateKey!))
+ // // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given");
+ // // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)");
+ // // }
+ // // }
+ // // finally {
+ // // Console.WriteLine($"Finished checking {room.RoomId}...");
+ // // }
+ // }
+ //
+ // try {
+ // var tasks = rooms.Select(room => WebWorkerService.TaskPool.Invoke(runKicks, room.RoomId, content)).ToList();
+ //
+ // await Task.WhenAll(tasks);
+ // }
+ // catch (Exception e) {
+ // Console.WriteLine(e);
+ // }
+
+ await NastyInternalsPleaseIgnore.ExecuteKickWithWasmWorkers(WebWorkerService, Homeserver, policy, data.Rooms);
+ // await Task.Run(async () => {
+ // foreach (var room in rooms) {
+ // try {
+ // Console.WriteLine($"Checking {room.RoomId}...");
+ // var members = await room.GetMembersListAsync();
+ // foreach (var member in members) {
+ // var membership = member.ContentAs<RoomMemberEventContent>();
+ // if (member.StateKey == room.Homeserver.WhoAmI.UserId) continue;
+ // if (membership?.Membership is "leave" or "ban") continue;
+ //
+ // if (content.EntityMatches(member.StateKey!))
+ // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given");
+ // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)");
+ // }
+ // ActiveKicks[policy]--;
+ // StateHasChanged();
+ // }
+ // finally {
+ // Console.WriteLine($"Finished checking {room.RoomId}...");
+ // }
+ // }
+ // });
+ }
+ finally {
+ ActiveKicks.Remove(policy);
+ StateHasChanged();
+ await Task.Delay(500);
+ }
+ }
+
+#region Nasty, nasty internals, please ignore!
+
+ private static class NastyInternalsPleaseIgnore {
+ public async static Task ExecuteKickWithWasmWorkers(WebWorkerService workerService, AuthenticatedHomeserverGeneric hs, StateEventResponse evt, List<string> roomIds) {
+ try {
+ // var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal, hs.WellKnownUris.Client, hs.AccessToken, roomId, content.Entity)).ToList();
+ var tasks = roomIds.Select(roomId => workerService.TaskPool.Invoke(ExecuteKickInternal2, hs.WellKnownUris, hs.AccessToken, roomId, evt)).ToList();
+ // workerService.TaskPool.Invoke(ExecuteKickInternal, hs.BaseUrl, hs.AccessToken, roomIds, content.Entity);
+ await Task.WhenAll(tasks);
+ }
+ catch (Exception e) {
+ Console.WriteLine(e);
+ }
+ }
+
+ private async static Task ExecuteKickInternal(string homeserverBaseUrl, string accessToken, string roomId, string entity) {
+ try {
+ Console.WriteLine("args: " + string.Join(", ", homeserverBaseUrl, accessToken, roomId, entity));
+ Console.WriteLine($"Checking {roomId}...");
+ var hs = new AuthenticatedHomeserverGeneric(homeserverBaseUrl, new() { Client = homeserverBaseUrl }, null, accessToken);
+ Console.WriteLine($"Got HS...");
+ var room = hs.GetRoom(roomId);
+ Console.WriteLine($"Got room...");
+ var members = await room.GetMembersListAsync();
+ Console.WriteLine($"Got members...");
+ // foreach (var member in members) {
+ // var membership = member.ContentAs<RoomMemberEventContent>();
+ // if (member.StateKey == hs.WhoAmI.UserId) continue;
+ // if (membership?.Membership is "leave" or "ban") continue;
+ //
+ // if (entity == member.StateKey)
+ // // await room.KickAsync(member.StateKey, content.Reason ?? "No reason given");
+ // Console.WriteLine($"Would kick {member.StateKey} from {room.RoomId} (EntityMatches)");
+ // }
+ }
+ catch (Exception e) {
+ Console.WriteLine(e);
+ }
+ }
+
+ private async static Task ExecuteKickInternal2(HomeserverResolverService.WellKnownUris wellKnownUris, string accessToken, string roomId, StateEventResponse policy) {
+ Console.WriteLine($"Checking {roomId}...");
+ Console.WriteLine(policy.EventId);
+ }
+ }
+
+#endregion
+
+#endregion
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor b/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor
index adac385..d5d0a7e 100644
--- a/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor
+++ b/MatrixUtils.Web/Pages/Rooms/PolicyList2.razor
@@ -76,6 +76,19 @@ else {
@if (policy.IsLegacyType) {
<LinkButton OnClick="@(() => RemovePolicyAsync(policy))">Update policy type</LinkButton>
}
+
+ @if (PolicyTypeIds[typeof(ServerPolicyRuleEventContent)].Contains(policy.EventId)) {
+ <LinkButton OnClick="@(() => { ServerPolicyToMakePermanent = policy; return Task.CompletedTask; })">Make permanent (wildcard)</LinkButton>
+ @if (CurrentUserIsDraupnir) {
+ <LinkButton OnClick="@(() => UpgradePolicyAsync(policy))">Kick matching users</LinkButton>
+ }
+ }
+ else {
+ <p>meow</p>
+ }
+ }
+ else {
+ <p>No permission to modify</p>
}
</div>
</div>
@@ -132,6 +145,7 @@ else {
private bool _enableAvatars;
private StateEventResponse? _currentlyEditingEvent;
+ private StateEventResponse? _serverPolicyToMakePermanent;
private Dictionary<Type, List<StateEventResponse>> PolicyEventsByType { get; set; } = new();
@@ -143,9 +157,18 @@ else {
}
}
+ private StateEventResponse? ServerPolicyToMakePermanent {
+ get => _serverPolicyToMakePermanent;
+ set {
+ _serverPolicyToMakePermanent = value;
+ StateHasChanged();
+ }
+ }
+
private AuthenticatedHomeserverGeneric Homeserver { get; set; }
private GenericRoom Room { get; set; }
private RoomPowerLevelEventContent PowerLevels { get; set; }
+ private bool CurrentUserIsDraupnir { get; set; }
protected override async Task OnInitializedAsync() {
var sw = Stopwatch.StartNew();
@@ -154,6 +177,7 @@ else {
if (Homeserver is null) return;
Room = Homeserver.GetRoom(RoomId!);
PowerLevels = (await Room.GetPowerLevelsAsync())!;
+ CurrentUserIsDraupnir = (await Homeserver.GetAccountDataOrNullAsync<object>("org.matrix.mjolnir.protected_rooms")) is not null;
await LoadStatesAsync();
Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
}
@@ -210,4 +234,7 @@ else {
private static Dictionary<string, Type> PolicyTypes = KnownPolicyTypes
.ToDictionary(x => x.GetCustomAttributes<MatrixEventAttribute>().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x);
+ private static Dictionary<Type, string[]> PolicyTypeIds = KnownPolicyTypes
+ .ToDictionary(x => x, x => x.GetCustomAttributes<MatrixEventAttribute>().Select(y => y.EventName).ToArray());
+
}
\ No newline at end of file
diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor
index 8745459..51f8e1b 100644
--- a/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor
+++ b/MatrixUtils.Web/Pages/Tools/Moderation/Draupnir/DraupnirProtectedRoomsEditor.razor
@@ -3,6 +3,7 @@
@page "/Tools/Moderation/Draupnir/ProtectedRoomsEditor"
@using System.Text.Json.Serialization
@using LibMatrix
+@using LibMatrix.EventTypes.Interop.Draupnir
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.RoomTypes
<h3>Edit Draupnir protected rooms</h3>
@@ -64,7 +65,7 @@
protected override async Task OnInitializedAsync() {
hs = await RMUStorage.GetCurrentSessionOrNavigate();
if (hs is null) return;
- data = await hs.GetAccountDataAsync<DraupnirProtectedRoomsData>("org.matrix.mjolnir.protected_rooms");
+ data = await hs.GetAccountDataAsync<DraupnirProtectedRoomsData>(DraupnirProtectedRoomsData.EventId);
StateHasChanged();
var tasks = (await hs.GetJoinedRooms()).Select(async room => {
var plTask = room.GetPowerLevelsAsync();
@@ -121,12 +122,7 @@
StateHasChanged();
}
-
- private class DraupnirProtectedRoomsData {
- [JsonPropertyName("rooms")]
- public List<string> Rooms { get; set; } = new();
- }
-
+
private class EditorRoomInfo {
public GenericRoom Room { get; set; }
public bool IsProtected { get; set; }
diff --git a/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
new file mode 100644
index 0000000..2d78f4e
--- /dev/null
+++ b/MatrixUtils.Web/Pages/Tools/Moderation/FindUsersByRegex.razor
@@ -0,0 +1,193 @@
+@page "/Tools/Moderation/FindUsersByRegex"
+@using System.Collections.Frozen
+@using ArcaneLibs.Extensions
+@using LibMatrix.RoomTypes
+@using System.Collections.ObjectModel
+@using System.Text.RegularExpressions
+@using LibMatrix
+@using LibMatrix.EventTypes.Spec.State
+@using LibMatrix.Filters
+@using LibMatrix.Helpers
+@using LibMatrix.Utilities
+<h3>Find users by regex</h3>
+<hr/>
+
+<p>Users (regex): </p>
+<InputTextArea @bind-Value="@UserIdString"></InputTextArea>
+
+<LinkButton OnClick="Execute">Execute</LinkButton>
+<br/>
+<LinkButton OnClick="RemoveKicks">Remove kicks</LinkButton>
+<LinkButton OnClick="RemoveBans">Remove bans</LinkButton>
+<br/>
+
+
+<details>
+ <summary>Results</summary>
+ @foreach (var (userId, events) in matches) {
+ <h4>@userId</h4>
+ <ul>
+ @foreach (var match in events) {
+ <li>
+ <ul>
+ <li>@match.RoomName (<span>@match.Room.RoomId</span>)</li>
+ <li>Membership: @(match.Event.RawContent.ToJson(indent: false)) (sent by @match.Event.Sender)</li>
+ </ul>
+ </li>
+ }
+ </ul>
+ }
+</details>
+
+<br/>
+@foreach (var line in log.Reverse()) {
+ <pre>@line</pre>
+}
+
+@code {
+
+ private ObservableCollection<string> log { get; set; } = new();
+
+ // List<RoomInfo> rooms { get; set; } = new();
+ List<GenericRoom> rooms { get; set; } = [];
+ Dictionary<string, List<Match>> matches = new();
+
+ private string UserIdString {
+ get => string.Join("\n", UserIDs);
+ set => UserIDs = value.Split("\n").Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
+ }
+
+ private List<string> UserIDs { get; set; } = new();
+
+ private AuthenticatedHomeserverGeneric hs { get; set; }
+
+ protected override async Task OnInitializedAsync() {
+ log.CollectionChanged += (sender, args) => StateHasChanged();
+ log.Add("Authenticating");
+ hs = await RMUStorage.GetCurrentSessionOrNavigate();
+ if (hs is null) return;
+
+ StateHasChanged();
+ Console.WriteLine("Rerendered!");
+ await base.OnInitializedAsync();
+ }
+
+ private async Task<string> Execute() {
+ log.Add("Constructing sync helper...");
+ var sh = new SyncHelper(hs) {
+ Filter = new SyncFilter() {
+ AccountData = new(types: []),
+ Presence = new(types: []),
+ Room = new() {
+ AccountData = new(types: []),
+ Ephemeral = new(types: []),
+ State = new(types: [RoomMemberEventContent.EventId]),
+ Timeline = new(types: []),
+ IncludeLeave = false
+ },
+ }
+ };
+
+ log.Add("Starting sync...");
+ var res = await sh.SyncAsync();
+
+ log.Add("Got sync response, parsing...");
+
+ var roomNames = (await Task.WhenAll((await hs.GetJoinedRooms()).Select(async room => { return (room.RoomId, await room.GetNameOrFallbackAsync()); }).ToList())).ToFrozenDictionary(x => x.Item1, x => x.Item2);
+
+ foreach (var userIdRegex in UserIDs) {
+ var regex = new Regex(userIdRegex, RegexOptions.Compiled);
+ log.Add($"Searching for {regex}:");
+ foreach (var (roomId, joinedRoom) in res.Rooms.Join) {
+ log.Add($"- Checking room {roomId}...");
+ foreach (var evt in joinedRoom.State.Events) {
+ if (evt.StateKey is null) continue;
+ if (evt.Type is not RoomMemberEventContent.EventId) continue;
+
+ if (regex.IsMatch(evt.StateKey)) {
+ log.Add($" - Found match in {roomId} for {evt.StateKey}");
+ if (!matches.ContainsKey(evt.StateKey)) {
+ matches[evt.StateKey] = new();
+ }
+
+ var room = hs.GetRoom(roomId);
+ matches[evt.StateKey].Add(new Match {
+ Room = room,
+ Event = evt,
+ RoomName = roomNames[roomId]
+ });
+ }
+ }
+ }
+ }
+
+ log.Add("Done!");
+
+ StateHasChanged();
+
+ return "";
+ }
+
+ public string? ImportFromRoomId { get; set; }
+
+ private async Task DoImportFromRoomId() {
+ try {
+ if (ImportFromRoomId is null) return;
+ var room = rooms.FirstOrDefault(x => x.RoomId == ImportFromRoomId);
+ UserIdString = string.Join("\n", (await room.GetMembersListAsync()).Select(x => x.StateKey));
+ }
+ catch (Exception e) {
+ Console.WriteLine(e);
+ log.Add("Could not fetch members list!\n" + e.ToString());
+ }
+
+ StateHasChanged();
+ }
+
+ private class Match {
+ public GenericRoom Room;
+ public StateEventResponse Event;
+ public string RoomName { get; set; }
+ }
+
+ private async IAsyncEnumerable<Match> GetMatches(string userId) {
+ var results = rooms.Select(async room => {
+ var state = await room.GetStateEventOrNullAsync(room.RoomId, userId);
+ if (state is not null) {
+ return new Match {
+ Room = room,
+ Event = state,
+ RoomName = await room.GetNameOrFallbackAsync()
+ };
+ }
+
+ return null;
+ }).ToAsyncEnumerable();
+ await foreach (var result in results) {
+ if (result is not null) {
+ yield return result;
+ }
+ }
+ }
+
+ private Task RemoveKicks() {
+ foreach (var (userId, matches) in matches) {
+ matches.RemoveAll(x => x.Event.ContentAs<RoomMemberEventContent>()!.Membership == "leave" && x.Event.Sender != x.Event.StateKey);
+ }
+
+ matches.RemoveAll((x, y) => y.Count == 0);
+ StateHasChanged();
+ return Task.CompletedTask;
+ }
+
+ private Task RemoveBans() {
+ foreach (var (userId, matches) in matches) {
+ matches.RemoveAll(x => x.Event.ContentAs<RoomMemberEventContent>()!.Membership == "ban" && x.Event.Sender != x.Event.StateKey);
+ }
+
+ matches.RemoveAll((x, y) => y.Count == 0);
+ StateHasChanged();
+ return Task.CompletedTask;
+ }
+
+}
\ No newline at end of file
|