diff --git a/MiniUtils.Core/RoomInviteHandler.cs b/MiniUtils.Core/RoomInviteHandler.cs
new file mode 100644
index 0000000..4597522
--- /dev/null
+++ b/MiniUtils.Core/RoomInviteHandler.cs
@@ -0,0 +1,92 @@
+using System.Text.Json;
+using ArcaneLibs;
+using ArcaneLibs.Extensions;
+using LibMatrix.Helpers;
+using LibMatrix.RoomTypes;
+using LibMatrix.Utilities.Bot.Interfaces;
+using Microsoft.Extensions.Logging;
+
+namespace MiniUtils.Core;
+
+public class RoomInviteHandler(ILogger<RoomInviteHandler> logger, AntiDmSpamConfiguration config) : IRoomInviteHandler {
+ public List<RoomInviteContext> Invites { get; } = [];
+
+ public List<Func<RoomInviteContext, Task>> OnInviteReceived { get; set; } = [];
+
+ private GenericRoom? LogRoom { get; set; }
+
+ public async Task HandleInviteAsync(RoomInviteContext invite) {
+ if (!string.IsNullOrWhiteSpace(config.LogRoom))
+ LogRoom = invite.Homeserver.GetRoom(config.LogRoom);
+
+ Invites.Add(invite);
+ await LogInvite(invite);
+ foreach (var handler in OnInviteReceived) {
+ try {
+ await handler.Invoke(invite);
+ }
+ catch (Exception e) {
+ if (!string.IsNullOrWhiteSpace(config.LogRoom)) {
+ var logRoom = invite.Homeserver.GetRoom(config.LogRoom);
+ await logRoom.SendMessageEventAsync(
+ new MessageBuilder()
+ .WithBody($"Failed to execute invite handler {handler}...").WithNewline()
+ .WithCollapsibleSection("Stack trace", msb => msb.WithCodeBlock(e.ToString(), "cs"))
+ .Build()
+ );
+ }
+ }
+ }
+ }
+
+ private async Task LogInvite(RoomInviteContext invite) {
+ logger.LogInformation("Received invite to {} from {}", invite.RoomId, invite.MemberEvent.Sender);
+
+ if (LogRoom is null) return;
+ var inviteData = invite.InviteData.ToJsonUtf8Bytes(ignoreNull: true);
+
+ var inviterNameTask = invite.TryGetInviterNameAsync();
+ var roomNameTask = invite.TryGetRoomNameAsync();
+ var inviteDataFileUriTask = invite.Homeserver.UploadFile(invite.RoomId + ".json", inviteData, "application/json");
+ logger.LogInformation("Uploaded invite data ({}) to {}", Util.BytesToString(inviteData.Length), await inviteDataFileUriTask);
+
+ await Task.WhenAll(inviterNameTask, roomNameTask, inviteDataFileUriTask);
+
+ var message = new MessageBuilder()
+ .WithBody("Received invite to ").WithMention(invite.RoomId, await roomNameTask).WithBody(" from ").WithMention(invite.MemberEvent.Sender!, await inviterNameTask)
+ .Build();
+
+ message.AdditionalData!["gay.rory.invite_logger.invite_data_uri"] = JsonDocument.Parse($"\"{await inviteDataFileUriTask}\"").RootElement;
+
+ await LogRoom.SendMessageEventAsync(message);
+
+ if (config.LogInviteDataAsFile) {
+ await LogRoom.SendMessageEventAsync(new() {
+ MessageType = "m.file",
+ Body = invite.RoomId + ".json",
+ FileName = invite.RoomId + ".json",
+ Url = await inviteDataFileUriTask,
+ FileInfo = new() { Size = inviteData.Length, MimeType = "application/json" }
+ });
+ }
+ }
+
+ public async Task RejectInvite(RoomInviteContext invite, MessageBuilder reason) {
+ if (LogRoom is not null)
+ _ = LogRoom.SendMessageEventAsync(reason.Build());
+
+ try {
+ await invite.Homeserver.GetRoom(invite.RoomId).LeaveAsync();
+ }
+ catch (Exception e) {
+ if (LogRoom is not null)
+ await LogRoom.SendMessageEventAsync(
+ new MessageBuilder().WithColoredBody("#FF8800", $"Failed to leave {invite.RoomId}:").WithNewline()
+ .WithCodeBlock(e.ToString(), "cs")
+ .Build()
+ );
+ }
+
+ Invites.Remove(invite);
+ }
+}
\ No newline at end of file
|