diff --git a/MiniUtils/Commands/DumpTimelineCommand.cs b/MiniUtils/Commands/DumpTimelineCommand.cs
index 4ee53c9..1139fb6 100644
--- a/MiniUtils/Commands/DumpTimelineCommand.cs
+++ b/MiniUtils/Commands/DumpTimelineCommand.cs
@@ -1,6 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using LibMatrix;
+using LibMatrix.Responses;
using LibMatrix.Services;
using LibMatrix.Utilities.Bot.Interfaces;
diff --git a/MiniUtils/Commands/IgnoreCommand.cs b/MiniUtils/Commands/IgnoreCommand.cs
index 4206b72..1bd4de3 100644
--- a/MiniUtils/Commands/IgnoreCommand.cs
+++ b/MiniUtils/Commands/IgnoreCommand.cs
@@ -44,23 +44,28 @@ public class IgnoreCommand(IgnoreListManager ignoreListManager) : ICommand {
await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
}
else if (ctx.Args is ["disable", .. var itemsToDisable]) {
- var count = await ignoreListManager.MoveList(false, itemsToDisable);
+ var count = await ignoreListManager.MoveList(false, itemsToDisable.Where(x => x.StartsWith('@')));
await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
}
else if (ctx.Args is ["enable", .. var itemsToEnable]) {
- var count = await ignoreListManager.MoveList(true, itemsToEnable);
+ var count = await ignoreListManager.MoveList(true, itemsToEnable.Where(x => x.StartsWith('@')));
await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
}
else if (ctx.Args is ["add", .. var itemsToAdd]) {
- var count = await ignoreListManager.AddList(itemsToAdd);
+ var count = await ignoreListManager.AddList(itemsToAdd.Where(x => x.StartsWith('@')));
+ await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
+ }
+
+ else if (ctx.Args is ["remove", .. var itemsToRemove]) {
+ var count = await ignoreListManager.RemoveList(itemsToRemove.Where(x => x.StartsWith('@')));
await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.RightArrowWithTail} {count}");
}
}
private async Task Summarize(CommandContext ctx, IgnoredUserListEventContentWithDisabled ignoreList) {
- var msb = new MessageBuilder()
- .WithBody($"Ignored users: {ignoreList.IgnoredUsers.Count}").WithNewline()
- .WithBody($"Disabled ignores: {ignoreList.DisabledIgnoredUsers.Count}").WithNewline();
- await ctx.Room.SendMessageEventAsync(msb.Build());
- }
- }
\ No newline at end of file
+ var msb = new MessageBuilder()
+ .WithBody($"Ignored users: {ignoreList.IgnoredUsers.Count}").WithNewline()
+ .WithBody($"Disabled ignores: {ignoreList.DisabledIgnoredUsers.Count}").WithNewline();
+ await ctx.Room.SendMessageEventAsync(msb.Build());
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/KickACLedCommand.cs b/MiniUtils/Commands/KickACLedCommand.cs
new file mode 100644
index 0000000..9ea8ec0
--- /dev/null
+++ b/MiniUtils/Commands/KickACLedCommand.cs
@@ -0,0 +1,73 @@
+using System.Collections.Frozen;
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Filters;
+using LibMatrix.Helpers;
+using LibMatrix.RoomTypes;
+using LibMatrix.Utilities.Bot.Interfaces;
+using MiniUtils.Classes;
+using MiniUtils.Services;
+
+namespace MiniUtils.Commands;
+
+public class KickACLedCommand(IgnoreListManager ignoreListManager) : ICommand {
+ public string Name => "kick acled users";
+
+ public string[]? Aliases => [];
+
+ public string Description => "Kick all users targetted by server ACLs";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ if (ctx.Args is ["banned"])
+ await RedactUsers(ctx, await ctx.Room.GetMemberIdsListAsync("ban"));
+ else if (ctx.Args is [.. var senders]) {
+ var sendersSet = senders.ToFrozenSet();
+ await RedactUsers(ctx, sendersSet);
+ }
+ }
+
+ private async Task RedactUsers(CommandContext ctx, FrozenSet<string> senders) {
+ var count = 0;
+ var subCount = 0;
+ List<Task> tasks = [];
+ // await foreach (var resp in ctx.Room.GetManyMessagesAsync(filter: filter.ToJson(false, ignoreNull: true), chunkSize: 1000)) {
+ // foreach (var chunk in resp.Chunk.Chunk(49)) {
+ // foreach (var evt in chunk) {
+ // if (!senders.Contains(evt.Sender!)) continue;
+ // tasks.Add(RedactEvent(ctx.Room, evt.EventId!));
+ // count++;
+ // subCount++;
+ // }
+ //
+ // if (subCount >= 40) {
+ // await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ // .WithBody(
+ // $"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {tasks.Count(t => t.IsCompletedSuccessfully)} {Emojis.Prohibited} {tasks.Count(t => t.IsFaulted)} {Emojis.Hourglass} {tasks.Count(t => t.Status == TaskStatus.Running)})")
+ // .Build());
+ // // await Task.WhenAll(tasks);
+ // subCount = 0;
+ // }
+ // }
+ // }
+
+ var acls = await ctx.Room.GetStateOrNullAsync<RoomServerAclEventContent>(RoomServerAclEventContent.EventId);
+ if (acls == null) {
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody("No ACLs found.").Build());
+ return;
+ }
+
+ await foreach (var resp in ctx.Room.GetMembersEnumerableAsync()) {
+ var serverName = resp.StateKey!.Split(':',2)[1];
+ if (acls.DenyRegexes?.Any(x => x.IsMatch(serverName)) ?? false) {
+ Console.WriteLine("Kicking {0} from {1} due to ACL match: {2}", resp.StateKey, ctx.Room.RoomId, acls.DenyRegexes.First(x => x.IsMatch(serverName)));
+ }
+ }
+
+ await Task.WhenAll(tasks);
+
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle} {count}").Build());
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/MakePolicyListCommand.cs b/MiniUtils/Commands/MakePolicyListCommand.cs
index 40b0695..b5639aa 100644
--- a/MiniUtils/Commands/MakePolicyListCommand.cs
+++ b/MiniUtils/Commands/MakePolicyListCommand.cs
@@ -8,9 +8,9 @@ using LibMatrix.Utilities.Bot.Interfaces;
namespace MiniUtils.Commands;
public class MakePolicyListCommand() : ICommand {
- public string Name => "makepolicylist";
+ public string Name => "make policy list";
- public string[]? Aliases => ["make policy list"];
+ public string[]? Aliases => ["makepolicylist"];
public string Description => "Make a new policy list";
diff --git a/MiniUtils/Commands/MakeRoomCommand.cs b/MiniUtils/Commands/MakeRoomCommand.cs
new file mode 100644
index 0000000..068bca5
--- /dev/null
+++ b/MiniUtils/Commands/MakeRoomCommand.cs
@@ -0,0 +1,235 @@
+using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Common;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Helpers;
+using LibMatrix.Responses;
+using LibMatrix.RoomTypes;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MiniUtils.Commands;
+
+public class MakeRoomCommand() : ICommand {
+ public string Name => "make room";
+
+ public string[]? Aliases => ["makeroom", "create room", "createroom"];
+
+ public string Description => "Make a new room";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ if (ctx.Args.Length == 0) {
+ await ctx.Room.SendMessageEventAsync(
+ new MessageBuilder()
+ .WithTable(tb => {
+ tb.WithTitle("~create room", 3);
+ tb.WithRow(rb => {
+ rb.WithCell("Argument")
+ .WithCell("Alternatives")
+ .WithCell("Description");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--alias <localpart>")
+ .WithCell("")
+ .WithCell("Set the room alias");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--avatar-url <url>")
+ .WithCell("")
+ .WithCell("Set the room avatar URL");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--copy-avatar [room]")
+ .WithCell("")
+ .WithCell("Copy the avatar from another room (or current room if unspecified)");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--copy-powerlevels [room]")
+ .WithCell("")
+ .WithCell("Copy the power levels from another room (or current room if unspecified)");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--invite-admin <user>")
+ .WithCell("")
+ .WithCell("Invite a user as an admin");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--invite <user>")
+ .WithCell("")
+ .WithCell("Invite a user");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--name <name>")
+ .WithCell("")
+ .WithCell("Set the room name");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--topic <topic>")
+ .WithCell("")
+ .WithCell("Set the room topic");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--federate <true|false>")
+ .WithCell("")
+ .WithCell("Set whether the room is federatable");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--join-rule <rule>")
+ .WithCell("""
+ --public
+ --invite-only
+ --knock
+ --restricted
+ --knock_restricted
+ --private
+ """)
+ .WithCell("Set the room join rule to public, invite-only, knock, restricted, knock-restricted or private");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--history-visibility <visibility>")
+ .WithCell("""
+ --shared
+ --invited
+ --joined
+ --world_readable
+ """)
+ .WithCell("Set the room history visibility to shared, invited, joined or world_readable");
+ });
+ })
+ .Build()
+ );
+ return;
+ }
+
+ var rb = new RoomBuilder() { };
+
+ for (int i = 0; i < ctx.Args.Length; i++) {
+ switch (ctx.Args[i]) {
+ case "--alias":
+ rb.AliasLocalPart = ctx.Args[++i];
+ break;
+ case "--avatar-url":
+ rb.Avatar!.Url = ctx.Args[++i];
+ break;
+ case "--copy-avatar": {
+ var room = await GetRoomByArgument(ctx, ctx.Args[i + 1]);
+ if (room != ctx.Room) i++;
+ rb.Avatar = await room.GetAvatarUrlAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have an avatar");
+ break;
+ }
+ case "--copy-powerlevels": {
+ var room = await GetRoomByArgument(ctx, ctx.Args[i + 1]);
+ if (room != ctx.Room) i++;
+ rb.PowerLevels = await room.GetPowerLevelsAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have power levels???");
+ break;
+ }
+ case "--invite-admin":
+ var inviteAdmin = ctx.Args[++i];
+ if (!inviteAdmin.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteAdmin);
+ }
+
+ rb.Invites.Add(inviteAdmin, "Marked explicitly as admin to be invited");
+ break;
+ case "--invite":
+ var inviteUser = ctx.Args[++i];
+ if (!inviteUser.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteUser);
+ }
+
+ rb.Invites.Add(inviteUser, "Marked explicitly to be invited");
+ break;
+ case "--name":
+ var nameEvt = rb.Name = new() { Name = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ nameEvt.Name += (nameEvt.Name.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--topic":
+ var topicEvt = rb.Topic = new() { Topic = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ topicEvt.Topic += (topicEvt.Topic.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--federate":
+ rb.IsFederatable = bool.Parse(ctx.Args[++i]);
+ break;
+ case "--public":
+ case "--invite-only":
+ case "--knock":
+ case "--restricted":
+ case "--knock_restricted":
+ case "--private":
+ rb.JoinRules.JoinRule = ctx.Args[i].Replace("--", "").ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite-only" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--join-rule":
+ if (i + 1 >= ctx.Args.Length || !ctx.Args[i + 1].StartsWith("--")) {
+ throw new ArgumentException("Expected join rule after --join-rule");
+ }
+
+ rb.JoinRules.JoinRule = ctx.Args[++i].ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--history-visibility":
+ rb.HistoryVisibility = new RoomHistoryVisibilityEventContent {
+ HistoryVisibility = ctx.Args[++i].ToLowerInvariant() switch {
+ "shared" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Shared,
+ "invited" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Invited,
+ "joined" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Joined,
+ "world_readable" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.WorldReadable,
+ _ => throw new ArgumentException("Unknown history visibility: " + ctx.Args[i])
+ }
+ };
+ break;
+
+ default:
+ throw new ArgumentException("Unknown argument: " + ctx.Args[i]);
+ }
+ }
+
+ // await ctx.Room.SendMessageEventAsync(
+ // new MessageBuilder()
+ // .WithCodeBlock(rb.ToJson(), "json")
+ // .Build()
+ // );
+ // var result = await ctx.Homeserver.CreateRoom(creationContent);
+ var result = await rb.Create(ctx.Homeserver);
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithMention($"{result.RoomId}?via={ctx.Homeserver.ServerName}", rb.CanonicalAlias.Alias)
+ .Build());
+ }
+
+ private async Task<GenericRoom> GetRoomByArgument(CommandContext ctx, string roomReference, bool defaultToCurrent = true) {
+ if (roomReference.StartsWith("--")) {
+ return defaultToCurrent ? ctx.Room : throw new ArgumentException("Invalid room reference: " + roomReference);
+ }
+
+ if (roomReference.StartsWith('!')) {
+ return ctx.Homeserver.GetRoom(roomReference);
+ }
+
+ if (roomReference.StartsWith('#')) {
+ var resolvedAlias = await ctx.Homeserver.ResolveRoomAliasAsync(roomReference);
+ return ctx.Homeserver.GetRoom(resolvedAlias.RoomId);
+ }
+
+ throw new ArgumentException("Invalid room reference: " + roomReference);
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/RedactCommand.cs b/MiniUtils/Commands/RedactCommand.cs
index e84191e..8aa65fc 100644
--- a/MiniUtils/Commands/RedactCommand.cs
+++ b/MiniUtils/Commands/RedactCommand.cs
@@ -1,6 +1,7 @@
using System.Collections.Frozen;
using ArcaneLibs.Extensions;
using LibMatrix;
+using LibMatrix.EventTypes.Spec;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Filters;
using LibMatrix.Helpers;
@@ -30,55 +31,83 @@ public class RedactCommand(IgnoreListManager ignoreListManager) : ICommand {
}
private async Task RedactUsers(CommandContext ctx, FrozenSet<string> senders) {
- var filter = new SyncFilter.EventFilter(senders: senders.ToList(), notTypes: ["m.room.redaction"]);
await ignoreListManager.MoveList(false, senders);
var count = 0;
- List<Task> tasks = [];
- await foreach (var resp in ctx.Room.GetManyMessagesAsync(filter: filter.ToJson(false, ignoreNull: true), chunkSize: 1000)) {
- foreach (var chunk in resp.Chunk.Chunk(49)) {
- foreach (var evt in chunk) {
- if (!senders.Contains(evt.Sender!)) continue;
- if(!await IsRedactionNeeded(ctx.Room, evt.EventId!, evt)) continue;
- tasks.Add(RedactEvent(ctx.Room, evt.EventId!));
- count++;
- }
-
- if (tasks.Count > 0) {
- await ctx.Room.SendMessageEventAsync(new MessageBuilder()
- .WithBody(
- $"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {tasks.Count(t => t.IsCompletedSuccessfully)} {Emojis.Prohibited} {tasks.Count(t => t.IsFaulted)} {Emojis.Hourglass} {tasks.Count(t => t.Status == TaskStatus.Running)})")
- .Build());
- // await Task.WhenAll(tasks);
+ // var subCount = 0;
+ // List<Task> tasks = [];
+ foreach (var senderChunk in senders.Chunk(10)) {
+ var filter = new SyncFilter.EventFilter(senders: senderChunk.ToList(), notTypes: ["m.room.redaction"]);
+
+ await foreach (var resp in ctx.Room.GetManyMessagesAsync(filter: filter.ToJson(false, ignoreNull: true), chunkSize: 1000)) {
+ // foreach (var chunk in resp.Chunk.Chunk(49)) {
+ // foreach (var evt in chunk) {
+ // if (!senders.Contains(evt.Sender!)) continue;
+ // if (!await IsRedactionNeeded(ctx.Room, evt.EventId!, evt)) continue;
+ // tasks.Add(RedactEvent(ctx.Room, evt.EventId!));
+ // count++;
+ // subCount++;
+ // }
+ //
+ // if (subCount >= 40) {
+ // await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ // .WithBody(
+ // $"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {tasks.Count(t => t.IsCompletedSuccessfully)} {Emojis.Prohibited} {tasks.Count(t => t.IsFaulted)} {Emojis.Hourglass} {tasks.Count(t => t.Status == TaskStatus.Running)})")
+ // .Build());
+ // // await Task.WhenAll(tasks);
+ // subCount = 0;
+ // }
+ // }
+ var toRedactQueryTask = resp.Chunk
+ .Where(x => senders.Contains(x.Sender!))
+ .Select(async x => (x, await IsRedactionNeeded(ctx.Room, x.EventId!, x)))
+ .ToList();
+ var toRedact = (await Task.WhenAll(toRedactQueryTask)).Where(x => x.Item2).Select(x => x.x).ToList();
+ foreach (var chunk in toRedact.Chunk(49)) {
+ var toSend = chunk.Select(x => new StateEvent() {
+ Type = RoomRedactionEventContent.EventId,
+ TypedContent = new RoomRedactionEventContent() {
+ Redacts = x.EventId
+ }
+ }).ToList();
+ toSend.Add(new StateEvent() {
+ Type = RoomMessageEventContent.EventId,
+ TypedContent =
+ new MessageBuilder()
+ .WithBody(
+ $"[{Emojis.Hourglass}] {Emojis.Recycle} {count} ({Emojis.Checkmark} {count} {Emojis.Hourglass} {toSend.Count})")
+ .Build()
+ });
+ count += toSend.Count - 1;
+ await ctx.Room.BulkSendEventsAsync(toSend);
}
}
}
- await Task.WhenAll(tasks);
-
+ // await Task.WhenAll(tasks);
+
await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle} {count}").Build());
// await ctx.Room.SendReactionAsync(ctx.MessageEvent.EventId!, $"{Emojis.Recycle} {count}");
}
private async Task<bool> IsRedactionNeeded(GenericRoom roomId, string eventId, StateEventResponse? evt = null) {
evt ??= await roomId.GetEventAsync(eventId);
-
+
// Ignore room member state events
if (evt is { StateKey: not null, Type: not RoomMemberEventContent.EventId }) return false;
-
+
// Ignore redaction events
if (evt is { Type: RoomRedactionEventContent.EventId }) return false;
-
+
// Ignore empty events
if (evt is { RawContent: null or { Count: 0 } }) return false;
-
+
// Ignore redacted events
if (evt.Unsigned?.ContainsKey("redacted_because") == true) return false;
-
-
- throw new NotImplementedException("Redaction check not implemented");
+ return true;
+ // throw new NotImplementedException("Redaction check not implemented");
}
-
+
private async Task RedactEvent(GenericRoom room, string eventId) {
bool success;
do {
diff --git a/MiniUtils/Commands/ServersCommand.cs b/MiniUtils/Commands/ServersCommand.cs
new file mode 100644
index 0000000..7e5c97a
--- /dev/null
+++ b/MiniUtils/Commands/ServersCommand.cs
@@ -0,0 +1,79 @@
+using System.Collections.Frozen;
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Filters;
+using LibMatrix.Helpers;
+using LibMatrix.Homeservers;
+using LibMatrix.Responses.Federation;
+using LibMatrix.RoomTypes;
+using LibMatrix.Services;
+using LibMatrix.Utilities.Bot.Interfaces;
+using MiniUtils.Classes;
+using MiniUtils.Services;
+
+namespace MiniUtils.Commands;
+
+public class ServersCommand(HomeserverProviderService hsProvider) : ICommand {
+ public string Name => "servers";
+
+ public string[]? Aliases => [];
+
+ public string Description => "Get a list of servers in the room";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ var lastUpdated = DateTime.Now;
+ var servers = (await ctx.Room.GetMembersByHomeserverAsync()).Keys.Select(GetServerVersionAsync).ToList().ToAsyncResultEnumerable();
+ Dictionary<object, List<string>> serverVersions = new();
+
+ var message = new MessageBuilder()
+ .WithBody($"[{Emojis.Hourglass}] {Emojis.Recycle} Gathering server versions, please wait...")
+ .Build();
+ var eventId = await ctx.Room.SendMessageEventAsync(message);
+
+ await foreach (var result in servers) {
+ if (!serverVersions.TryGetValue(result, out var serverNames)) {
+ serverNames = [];
+ serverVersions[result] = serverNames;
+ }
+
+ serverNames.Add(result.Server);
+
+ if (DateTime.Now - lastUpdated > TimeSpan.FromMilliseconds(500)) {
+ lastUpdated = DateTime.Now;
+ var msb = new MessageBuilder()
+ .WithBody($"[{Emojis.Hourglass}] {serverVersions.Count} servers found so far:").WithNewline();
+
+ foreach (var (res, serversByVersion) in serverVersions.Where(x => x.Key is ServerVersionResponse)) {
+ var svr = (ServerVersionResponse)res;
+ msb.WithBody($"- {svr.Server.Name} {svr.Server.Version}: {string.Join(", ", serversByVersion)}").WithNewline();
+ }
+
+ await ctx.Room.SendMessageEventAsync(
+ msb.Build()
+ .SetReplaceRelation<RoomMessageEventContent>(eventId.EventId)
+ );
+ }
+ }
+
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithBody($"[{Emojis.Bullseye}] {serverVersions.Count} servers found:")
+ .Build()
+ .SetReplaceRelation<RoomMessageEventContent>(eventId.EventId)
+ );
+ }
+
+ private async Task<(string Server, object Result)> GetServerVersionAsync(string server) {
+ try {
+ var hs = await hsProvider.GetRemoteHomeserver(server);
+ var version = await hs.FederationClient.GetServerVersionAsync();
+ return (server, version);
+ }
+ catch (Exception e) {
+ return (server, e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MiniUtils/Commands/SpamCommand.cs b/MiniUtils/Commands/SpamCommand.cs
index 742ae3b..01f7244 100644
--- a/MiniUtils/Commands/SpamCommand.cs
+++ b/MiniUtils/Commands/SpamCommand.cs
@@ -1,3 +1,8 @@
+using System.Net.Http.Json;
+using LibMatrix;
+using LibMatrix.EventTypes.Spec.State.Policy;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Extensions;
using LibMatrix.Helpers;
using LibMatrix.RoomTypes;
using LibMatrix.Utilities.Bot.Interfaces;
@@ -16,11 +21,62 @@ public class SpamCommand(IgnoreListManager ignoreListManager) : ICommand {
public bool Unlisted => true;
public async Task Invoke(CommandContext ctx) {
- var tasks = Enumerable.Range(0, 10000)
- .Select(i => SendMessage(ctx.Room, i.ToString()))
- .ToList();
- await Task.WhenAll(tasks);
- await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle}").Build());
+ // var tasks = Enumerable.Range(0, 10000)
+ // .Select(i => SendMessage(ctx.Room, i.ToString()))
+ // .ToList();
+ // await Task.WhenAll(tasks);
+ // await ctx.Room.SendMessageEventAsync(new MessageBuilder().WithBody($"{Emojis.Recycle}").Build());
+ //
+ for (int i = 0; i < 8; i++) {
+ // _ = ctx.Homeserver.ClientHttpClient.PostAsJsonAsync($"/_matrix/client/unstable/gay.rory.bulk_send_events/rooms/{ctx.Room.RoomId}/bulk_send_events",
+ // CreateMessagesAsync());
+
+ // await new MatrixHttpClient(){BaseAddress = new("http://127.0.0.1:8888")}.GetAsync($"/_matrix/client/unstable/gay.rory.bulk_send_events/rooms/{ctx.Room.RoomId}/bulk_send_events");
+ _ = ctx.Homeserver.ClientHttpClient
+ // _ = new MatrixHttpClient(){BaseAddress = new("http://127.0.0.1:8888")}
+ .PostAsyncEnumerableAsJsonAsync($"/_matrix/client/unstable/gay.rory.bulk_send_events/rooms/{ctx.Room.RoomId}/bulk_send_events?_r={Guid.NewGuid()}",
+ CreateMessagesAsync(ctx.Room));
+ }
+ }
+
+ private async IAsyncEnumerable<StateEvent> CreateMessagesAsync(GenericRoom room) {
+ int i = 0;
+ // var pls = await room.GetStateEventAsync(RoomPowerLevelEventContent.EventId);
+ // var pls2 = await room.GetStateEventAsync(RoomPowerLevelEventContent.EventId);
+ // if (pls2.TypedContent is RoomPowerLevelEventContent pl2) {
+ // pl2.Ban = 5;
+ // pl2.Users!["@emma:synapse.localhost"] = 102;
+ // pls2.TypedContent = pl2;
+ // }
+ //
+ // yield return new() {
+ // RawContent = pls2.RawContent,
+ // Type = RoomPowerLevelEventContent.EventId,
+ // StateKey = ""
+ // // StateKey = Guid.NewGuid().ToString()
+ // };
+ while (i++ < 200) {
+ // await Task.Delay(500);
+ Console.WriteLine(i);
+ // yield return new() {
+ // Type = "m.room.message",
+ // TypedContent = new MessageBuilder().WithBody(i.ToString()).Build()
+ // };
+
+ // yield return new() {
+ // RawContent = pls.RawContent,
+ // Type = RoomPowerLevelEventContent.EventId,
+ // StateKey = ""
+ // // StateKey = Guid.NewGuid().ToString()
+ // };
+ yield return new() {
+ TypedContent = new UserPolicyRuleEventContent() {
+ Entity = $"@{Guid.NewGuid()}:{room.Homeserver.ServerName}",
+ },
+ Type = UserPolicyRuleEventContent.EventId,
+ StateKey = Guid.NewGuid().ToString()
+ };
+ }
}
private async Task SendMessage(GenericRoom room, string content) {
diff --git a/MiniUtils/MiniUtils.csproj b/MiniUtils/MiniUtils.csproj
index f7af751..5ac38cb 100644
--- a/MiniUtils/MiniUtils.csproj
+++ b/MiniUtils/MiniUtils.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
- <TargetFramework>net9.0</TargetFramework>
+ <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
@@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2"/>
+ <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.2.25502.107" />
</ItemGroup>
<ItemGroup>
diff --git a/MiniUtils/Program.cs b/MiniUtils/Program.cs
index acf1902..612b4ad 100644
--- a/MiniUtils/Program.cs
+++ b/MiniUtils/Program.cs
@@ -1,7 +1,9 @@
using LibMatrix.Extensions;
+using LibMatrix.Helpers;
using LibMatrix.Services;
using LibMatrix.Utilities.Bot;
using MiniUtils;
+using MiniUtils.Classes;
using MiniUtils.Core;
using MiniUtils.Services;
using MiniUtils.Utilities;
@@ -11,7 +13,16 @@ var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddRoryLibMatrixServices()
.AddMatrixBot()
.AddCommandHandler()
- .DiscoverAllCommands();
+ .DiscoverAllCommands()
+ .WithCommandResultHandler(async result => {
+ if(result.Exception is not null)
+ await result.Context.Room.SendMessageEventAsync(
+ new MessageBuilder()
+ .WithBody($"[{Emojis.Prohibited}] An error occurred while processing your command: {result.Exception.Message}")
+ .WithNewline().WithCodeBlock(result.Exception.ToString(), "csharp")
+ .Build()
+ );
+ });
builder.Services.AddSingleton<MiniUtilsConfiguration>();
builder.Services.AddSingleton<MscInfoProvider>();
@@ -20,6 +31,8 @@ builder.Services.AddSingleton<IgnoreListManager>();
builder.Services.AddHostedService<MiniUtilsWorker>();
builder.Services.AddHostedService<AutoTombstoneFollowerService>();
+
+
// builder.Services.AddSingleton<PolicyStore>();
// MatrixHttpClient.LogRequests = false;
diff --git a/MiniUtils/Services/IgnoreListManager.cs b/MiniUtils/Services/IgnoreListManager.cs
index 3b6dc96..cb863a8 100644
--- a/MiniUtils/Services/IgnoreListManager.cs
+++ b/MiniUtils/Services/IgnoreListManager.cs
@@ -65,7 +65,7 @@ public class IgnoreListManager(AuthenticatedHomeserverGeneric homeserver) {
return moved;
}
- public async Task<int> AddList(string[] itemsToAdd) {
+ public async Task<int> AddList(IEnumerable<string> itemsToAdd) {
int added = 0;
await Lock.WaitAsync();
var ignoreList = await homeserver.GetAccountDataOrNullAsync<IgnoredUserListEventContentWithDisabled>(IgnoredUserListEventContent.EventId) ?? new();
@@ -76,12 +76,30 @@ public class IgnoreListManager(AuthenticatedHomeserverGeneric homeserver) {
added++;
continue;
}
+
ignoreList.IgnoredUsers.Add(item, new());
added++;
}
+
if (added > 0)
await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreList);
Lock.Release();
return added;
}
+
+ public async Task<int> RemoveList(IEnumerable<string> itemsToRemove) {
+ int removed = 0;
+ await Lock.WaitAsync();
+ var ignoreList = await homeserver.GetAccountDataOrNullAsync<IgnoredUserListEventContentWithDisabled>(IgnoredUserListEventContent.EventId) ?? new();
+ foreach (var item in itemsToRemove) {
+ if (ignoreList.IgnoredUsers.Remove(item)) removed++;
+ if (ignoreList.DisabledIgnoredUsers.Remove(item)) removed++;
+ removed++;
+ }
+
+ if (removed > 0)
+ await homeserver.SetAccountDataAsync(IgnoredUserListEventContent.EventId, ignoreList);
+ Lock.Release();
+ return removed;
+ }
}
\ No newline at end of file
diff --git a/MiniUtils/appsettings.Development.json b/MiniUtils/appsettings.Development.json.h
index 4cfb975..4cfb975 100644
--- a/MiniUtils/appsettings.Development.json
+++ b/MiniUtils/appsettings.Development.json.h
|