about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-11-10 03:05:18 +0100
committerRory& <root@rory.gay>2025-11-10 03:05:18 +0100
commitd272c5b6f50e3f66a430fe4a6bbb3188f9adb1c8 (patch)
tree328d96076fe8c6fe6081f9083fcb3a498c41a683
parentMore stuff (diff)
downloadMiniUtils-master.tar.xz
MiniUtils changes, add uick ban sync hack bot HEAD master
-rw-r--r--.idea/.idea.MiniUtils/.idea/vcs.xml2
m---------LibMatrix0
-rw-r--r--MiniUtils.CSync/Emojis.cs17
-rw-r--r--MiniUtils.CSync/MiniUtils.CSync.csproj18
-rw-r--r--MiniUtils.CSync/Program.cs10
-rw-r--r--MiniUtils.CSync/Properties/launchSettings.json12
-rw-r--r--MiniUtils.CSync/Worker.cs48
-rw-r--r--MiniUtils.CSync/appsettings.Development.json (renamed from MiniUtils/appsettings.Development.json)0
-rw-r--r--MiniUtils.CSync/appsettings.json8
-rw-r--r--MiniUtils.Core/MiniUtils.Core.csproj4
-rw-r--r--MiniUtils.sln64
-rw-r--r--MiniUtils.sln.DotSettings.user10
-rw-r--r--MiniUtils/Commands/DumpTimelineCommand.cs1
-rw-r--r--MiniUtils/Commands/IgnoreCommand.cs23
-rw-r--r--MiniUtils/Commands/KickACLedCommand.cs73
-rw-r--r--MiniUtils/Commands/MakePolicyListCommand.cs4
-rw-r--r--MiniUtils/Commands/MakeRoomCommand.cs235
-rw-r--r--MiniUtils/Commands/RedactCommand.cs83
-rw-r--r--MiniUtils/Commands/ServersCommand.cs79
-rw-r--r--MiniUtils/Commands/SpamCommand.cs66
-rw-r--r--MiniUtils/MiniUtils.csproj4
-rw-r--r--MiniUtils/Program.cs15
-rw-r--r--MiniUtils/Services/IgnoreListManager.cs20
-rw-r--r--MiniUtils/appsettings.Development.json.h53
24 files changed, 789 insertions, 60 deletions
diff --git a/.idea/.idea.MiniUtils/.idea/vcs.xml b/.idea/.idea.MiniUtils/.idea/vcs.xml

index 94a25f7..0347848 100644 --- a/.idea/.idea.MiniUtils/.idea/vcs.xml +++ b/.idea/.idea.MiniUtils/.idea/vcs.xml
@@ -2,5 +2,7 @@ <project version="4"> <component name="VcsDirectoryMappings"> <mapping directory="$PROJECT_DIR$" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/LibMatrix" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/LibMatrix/ArcaneLibs" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/LibMatrix b/LibMatrix -Subproject 28adb35ab9b6905eebcd83b6caa1b12d49b26be +Subproject 6af19f2d27739e9cecaf6bab13a92b5705aba2f diff --git a/MiniUtils.CSync/Emojis.cs b/MiniUtils.CSync/Emojis.cs new file mode 100644
index 0000000..aef8904 --- /dev/null +++ b/MiniUtils.CSync/Emojis.cs
@@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; + +namespace MiniUtils.Classes; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class Emojis { + // Useful page: https://www.compart.com/en/unicode + + public const string ThumbsUp = "\ud83d\udc4d\ufe0e"; + public const string Recycle = "\u267b\ufe0e"; + public const string Bullseye = "\u25ce\ufe0e"; + public const string RightArrowWithTail = "\u21a3\ufe0e"; + public const string Prohibited = "\ud83d\udec7\ufe0e"; + public const string Wastebasket = "\ud83d\uddd1\ufe0e"; + public const string Hourglass = "\u231b\ufe0e"; + public const string Checkmark = "\u2705\ufe0e"; +} \ No newline at end of file diff --git a/MiniUtils.CSync/MiniUtils.CSync.csproj b/MiniUtils.CSync/MiniUtils.CSync.csproj new file mode 100644
index 0000000..c76d186 --- /dev/null +++ b/MiniUtils.CSync/MiniUtils.CSync.csproj
@@ -0,0 +1,18 @@ +<Project Sdk="Microsoft.NET.Sdk.Worker"> + + <PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <Nullable>enable</Nullable> + <ImplicitUsings>enable</ImplicitUsings> + <UserSecretsId>dotnet-MiniUtils.CSync-9083faba-cbe2-48f2-b201-eca73a4050e9</UserSecretsId> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-rc.2.25502.107" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\LibMatrix\LibMatrix\LibMatrix.csproj" /> + <ProjectReference Include="..\LibMatrix\Utilities\LibMatrix.Utilities.Bot\LibMatrix.Utilities.Bot.csproj" /> + </ItemGroup> +</Project> diff --git a/MiniUtils.CSync/Program.cs b/MiniUtils.CSync/Program.cs new file mode 100644
index 0000000..a86852c --- /dev/null +++ b/MiniUtils.CSync/Program.cs
@@ -0,0 +1,10 @@ +using LibMatrix.Services; +using LibMatrix.Utilities.Bot; +using MiniUtils.CSync; + +var builder = Host.CreateApplicationBuilder(args); +builder.Services.AddHostedService<Worker>(); +builder.Services.AddRoryLibMatrixServices().AddMatrixBot(); + +var host = builder.Build(); +host.Run(); diff --git a/MiniUtils.CSync/Properties/launchSettings.json b/MiniUtils.CSync/Properties/launchSettings.json new file mode 100644
index 0000000..9985ad5 --- /dev/null +++ b/MiniUtils.CSync/Properties/launchSettings.json
@@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "MiniUtils.CSync": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/MiniUtils.CSync/Worker.cs b/MiniUtils.CSync/Worker.cs new file mode 100644
index 0000000..b2f8d58 --- /dev/null +++ b/MiniUtils.CSync/Worker.cs
@@ -0,0 +1,48 @@ +using LibMatrix; +using LibMatrix.EventTypes.Spec.State.Policy; +using LibMatrix.EventTypes.Spec.State.RoomInfo; +using LibMatrix.Helpers; +using LibMatrix.Homeservers; +using LibMatrix.RoomTypes; +using MiniUtils.Classes; + +namespace MiniUtils.CSync; + +public class Worker(ILogger<Worker> logger, AuthenticatedHomeserverGeneric hs) : BackgroundService { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + while (!stoppingToken.IsCancellationRequested) { + var cme = hs.GetRoom("!fTjMjIzNKEsFlUIiru:neko.dev"); + var targetRoom = hs.GetRoom("!kiP76YI0AlDqXjcKj2nM_Bq30GCgGWehndSt3iyA85E"); + + var policies = (await cme.GetFullStateAsListAsync()).Where(x => x is { Type: UserPolicyRuleEventContent.EventId, RawContent.Count: > 1 }) + .Select(x => x.ContentAs<UserPolicyRuleEventContent>()) + .ToList(); + var members = await targetRoom.GetMemberIdsListAsync(membership: "join"); + var intersected = members.Where(x => policies.Any(p => p!.Entity == x)).ToList(); + logger.LogInformation("Found {count} members matching policies", intersected.Count); + + if (intersected.Count > 0) { + await targetRoom.BulkSendEventsAsync(intersected.Select(x => new StateEvent() { + Type = RoomMemberEventContent.EventId, + StateKey = x, + TypedContent = new RoomMemberEventContent() { + Membership = "ban", + Reason = "spam" + } + })); + await targetRoom.SendMessageEventAsync(new MessageBuilder() + .WithBody($"[Pagination helper - {Emojis.Wastebasket}] Banned {intersected.Count} users") + .Build()); + // await RedactEventsAsync() + } + + await Task.Delay(5000, stoppingToken); + } + } + + private async Task RedactEventsAsync(List<string> userIds, GenericRoom room) { + for (int i = 0; i < 10; i++) { + + } + } +} \ No newline at end of file diff --git a/MiniUtils/appsettings.Development.json b/MiniUtils.CSync/appsettings.Development.json
index 4cfb975..4cfb975 100644 --- a/MiniUtils/appsettings.Development.json +++ b/MiniUtils.CSync/appsettings.Development.json
diff --git a/MiniUtils.CSync/appsettings.json b/MiniUtils.CSync/appsettings.json new file mode 100644
index 0000000..b2dcdb6 --- /dev/null +++ b/MiniUtils.CSync/appsettings.json
@@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/MiniUtils.Core/MiniUtils.Core.csproj b/MiniUtils.Core/MiniUtils.Core.csproj
index 6e9363f..d0b0f4c 100644 --- a/MiniUtils.Core/MiniUtils.Core.csproj +++ b/MiniUtils.Core/MiniUtils.Core.csproj
@@ -1,14 +1,14 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> + <TargetFramework>net10.0</TargetFramework> <LangVersion>preview</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0-preview.3.25171.5" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0-rc.2.25502.107" /> </ItemGroup> <ItemGroup> diff --git a/MiniUtils.sln b/MiniUtils.sln
index 188112f..2c06257 100644 --- a/MiniUtils.sln +++ b/MiniUtils.sln
@@ -44,6 +44,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Utilities.Bot", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniUtils.Core", "MiniUtils.Core\MiniUtils.Core.csproj", "{5A4F5F8A-3638-4E3C-B20D-4F651EA165DD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.Federation", "LibMatrix\LibMatrix.Federation\LibMatrix.Federation.csproj", "{7BBE090D-3EB6-47AB-B344-B4B32195E3BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibMatrix.FederationTest", "LibMatrix\Utilities\LibMatrix.FederationTest\LibMatrix.FederationTest.csproj", "{3405E10B-B86C-4495-B501-DBE5F4F99BF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniUtils.CSync", "MiniUtils.CSync\MiniUtils.CSync.csproj", "{9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -294,6 +300,42 @@ Global {5A4F5F8A-3638-4E3C-B20D-4F651EA165DD}.Release|x64.Build.0 = Release|Any CPU {5A4F5F8A-3638-4E3C-B20D-4F651EA165DD}.Release|x86.ActiveCfg = Release|Any CPU {5A4F5F8A-3638-4E3C-B20D-4F651EA165DD}.Release|x86.Build.0 = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|x64.Build.0 = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Debug|x86.Build.0 = Debug|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|Any CPU.Build.0 = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|x64.ActiveCfg = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|x64.Build.0 = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|x86.ActiveCfg = Release|Any CPU + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA}.Release|x86.Build.0 = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|x64.Build.0 = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Debug|x86.Build.0 = Debug|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|Any CPU.Build.0 = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|x64.ActiveCfg = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|x64.Build.0 = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|x86.ActiveCfg = Release|Any CPU + {3405E10B-B86C-4495-B501-DBE5F4F99BF6}.Release|x86.Build.0 = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|x64.ActiveCfg = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|x64.Build.0 = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|x86.ActiveCfg = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Debug|x86.Build.0 = Debug|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|Any CPU.Build.0 = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|x64.ActiveCfg = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|x64.Build.0 = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|x86.ActiveCfg = Release|Any CPU + {9AA6A2E0-0895-4964-BDB4-CC0BE85C5375}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -308,15 +350,17 @@ Global {7CF6BBC9-2176-452A-A55F-A315F611923A} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} {C1F04A37-F946-4AA9-872D-A38A289F41BE} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} {545A47FC-6BAA-4C92-BAD1-C1ED413077BC} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {59745535-3681-423D-AE8F-5C08D4671ADC} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {6FE1B566-08DF-4BC0-AC2F-F9176AA17087} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {F04E9976-C303-4DEA-AA8D-EEDE6DF44069} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {A61BAD5E-77AC-49A0-8028-5E84EF990987} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {E6F92ED7-6EE2-4D71-8962-15613CD379BC} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {3D5DDCB4-3840-4ACE-AB17-A217F3A27F50} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {272B0A44-B985-47FF-8C93-32A77F5919C9} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {D852D486-F1DA-48A6-BBAF-43FF3396527D} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {6C5E7FB8-D6E8-4CBD-95FC-89EAB773472A} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} - {E526424D-ADC7-41DB-AD63-E359AC2954D9} = {17E2FB3F-0F61-3CDC-2874-2686F1726316} + {59745535-3681-423D-AE8F-5C08D4671ADC} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {6FE1B566-08DF-4BC0-AC2F-F9176AA17087} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {F04E9976-C303-4DEA-AA8D-EEDE6DF44069} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {A61BAD5E-77AC-49A0-8028-5E84EF990987} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {E6F92ED7-6EE2-4D71-8962-15613CD379BC} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {3D5DDCB4-3840-4ACE-AB17-A217F3A27F50} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {272B0A44-B985-47FF-8C93-32A77F5919C9} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {D852D486-F1DA-48A6-BBAF-43FF3396527D} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {6C5E7FB8-D6E8-4CBD-95FC-89EAB773472A} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {E526424D-ADC7-41DB-AD63-E359AC2954D9} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {7BBE090D-3EB6-47AB-B344-B4B32195E3BA} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} + {3405E10B-B86C-4495-B501-DBE5F4F99BF6} = {6C4179B9-BFAA-0403-502F-9DAF28C26A6E} EndGlobalSection EndGlobal diff --git a/MiniUtils.sln.DotSettings.user b/MiniUtils.sln.DotSettings.user
index f543792..caf48e9 100644 --- a/MiniUtils.sln.DotSettings.user +++ b/MiniUtils.sln.DotSettings.user
@@ -3,16 +3,24 @@ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConfigurationBinder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8feb37d1c2bc4bb7aba846da979b825aaf20_003F20_003Fa7159e0d_003FConfigurationBinder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultInterpolatedStringHandler_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003F38_003F707b550d_003FDefaultInterpolatedStringHandler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe443585619b64bcd8252486eca6648c078a00_003F5c_003F555f35de_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F913c179e36c64426ac5fcfffcc9268b278a00_003F89_003F537d998d_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F97_003Ffb30ee1f_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003Fc1_003F72e4c91c_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fba81a6af46624b56ac6210bdbcc99af2d19e00_003F7d_003F77f9c40b_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHashtable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003Fd2_003F20632896_003FHashtable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpResponseMessage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fdc2c4f9d0a9d4546b54cbaedf35715951a1e00_003F2f_003F4be0d618_003FHttpResponseMessage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJsonContent_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F82a6262f326341f784e01e686c8860131b800_003F85_003F1952a932_003FJsonContent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJsonSerializerOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4a803072a1ec4b5cb542160d01f4125a16d400_003Fcc_003F8618e224_003FJsonSerializerOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F65_003Fb77a719c_003FList_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003F2f_003F707c45aa_003FList_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObjectExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8fd5e96d6574456095123be1ecfbdfa914200_003Fe2_003F3561a383_003FObjectExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APipeOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6abeb72143fa4b84be432034a29f92d229400_003Fec_003F8fb684db_003FPipeOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARune_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003F50_003F2cf1d657_003FRune_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollectionHostedServiceExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff13297a632424a6abffea4dd75a36a75d128_003Ff9_003Fb35aae11_003FServiceCollectionHostedServiceExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStackFrameIterator_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F86_003F8b4aa64e_003FStackFrameIterator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStackFrameIterator_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003Fc5_003Fbce9c992_003FStackFrameIterator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStreamContent_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fed59b04e6e114d64aa0f92cbdff4d26e1a1e00_003F1a_003Fa146de84_003FStreamContent_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATaskAwaiter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fea51ca5e833244688d7ca912cfc70784d19c00_003F10_003F04572d58_003FTaskAwaiter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> - <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe443585619b64bcd8252486eca6648c078a00_003Faf_003Fe6779903_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ No newline at end of file + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelpers_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ff1b929573c264d7a81f261ae2f951019d19e00_003F5c_003F317c3cdc_003FThrowHelpers_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fe443585619b64bcd8252486eca6648c078a00_003Faf_003Fe6779903_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUtf8JsonWriter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4a803072a1ec4b5cb542160d01f4125a16d400_003Fdf_003F9b020bcf_003FUtf8JsonWriter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ No newline at end of file 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.h b/MiniUtils/appsettings.Development.json.h new file mode 100644
index 0000000..4cfb975 --- /dev/null +++ b/MiniUtils/appsettings.Development.json.h
@@ -0,0 +1,53 @@ +{ + // Don't touch this unless you know what you're doing: + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "LibMatrixBot": { + // Homeserver to connect to. + // Note: Homeserver resolution is applied here, but a direct base URL can be used. + "Homeserver": "rory.gay", + + // Absolute path to the file containing the access token + "AccessTokenPath": "/home/Rory/matrix_access_token", + "InviteHandler": { + "SyncConfiguration": { + // How long to wait until the sync request times out + // "Timeout": 300000, + + // Minimum sync interval, useful if you want to have less traffic than a normal client. + "MinimumSyncTime": "00:00:10.000", + + // What presence value to set + // Defaults to "online" if null or not set + // "Presence": "online", + + // Filter to apply to the sync request. Useful if you want custom data to be sent. + // "Filter": { }, + + // Whether to initial sync on startup - very useful in development, or just to be sure. + "InitialSyncOnStartup": true + } + } + }, + "AntiDmSpam": { + // Whether invites should be logged to a room. + "LogRoom": "!GrLSwdAkdrvfMrRYKR:rory.gay", + "LogInviteDataAsFile": true, + // Whether to report users and rooms when an invite is blocked. + "ReportBlockedInvites": true, + // WARNING: If you're a room moderator, this will cause your client to not receive events from ignored users! + "IgnoreBannedUsers": true, + // Policy lists to follow + "PolicyLists": [ + { + "Name": "Community Moderation Effort", + "RoomId": "!fTjMjIzNKEsFlUIiru:neko.dev", + "Vias": [ "rory.gay" ] + } + ] + } +} \ No newline at end of file