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.CSync/appsettings.Development.json b/MiniUtils.CSync/appsettings.Development.json
new file mode 100644
index 0000000..4cfb975
--- /dev/null
+++ b/MiniUtils.CSync/appsettings.Development.json
@@ -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
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"
+ }
+ }
+}
|