using System.Text.Json; using System.Text.Json.Nodes; using ArcaneLibs; using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec.State.RoomInfo; using LibMatrix.Helpers; using LibMatrix.Utilities.Bot.Services; using MatrixInviteLogger; public class InviteLogger(ILogger logger, InviteLoggerConfiguration config) : InviteHandlerHostedService.IInviteHandler { public async Task HandleInviteAsync(InviteHandlerHostedService.InviteEventArgs invite) { logger.LogInformation("Received invite to room {}", invite.RoomId); var logRoom = invite.Homeserver.GetRoom(config.LogRoom); var inviterName = await GetInviterNameAsync(invite); string roomName = await GetRoomNameAsync(invite); logger.LogInformation("Inviter: {}, Room: {}", inviterName, roomName); var message = new MessageBuilder() .WithBody("Received invite to ").WithMention(invite.RoomId, roomName).WithBody(" from ").WithMention(invite.MemberEvent.Sender!, inviterName) .Build(); // TODO: can we filter this somehow to stay within event size limits? // var serialisedInviteData = JsonNode.Parse(invite.InviteData.ToJson(ignoreNull: true)); // message.AdditionalData!["gay.rory.invite_logger.invite_data"] = serialisedInviteData!; var inviteData = invite.InviteData.ToJsonUtf8Bytes(ignoreNull: true); var inviteDataFileUri = await invite.Homeserver.UploadFile(invite.RoomId + ".json", inviteData, "application/json"); logger.LogInformation("Uploaded invite data ({}) to {}", Util.BytesToString(inviteData.Length), inviteDataFileUri); // Dictionary message.AdditionalData!["gay.rory.invite_logger.invite_data_uri"] = JsonDocument.Parse($"\"{inviteDataFileUri}\"").RootElement; await logRoom.SendMessageEventAsync(message); if (config.SendInviteDataAsFile) { await logRoom.SendMessageEventAsync(new() { MessageType = "m.file", Body = invite.RoomId + ".json", FileName = invite.RoomId + ".json", Url = inviteDataFileUri, FileInfo = new() { Size = inviteData.Length, MimeType = "application/json" } }); } } private async Task GetInviterNameAsync(InviteHandlerHostedService.InviteEventArgs invite) { var name = invite.InviteData.InviteState?.Events? .FirstOrDefault(evt => evt is { Type: RoomMemberEventContent.EventId } && evt.StateKey == invite.MemberEvent.Sender)? .ContentAs()?.DisplayName; if (!string.IsNullOrWhiteSpace(name)) return name; try { await invite.Homeserver.GetProfileAsync(invite.MemberEvent.Sender!); } catch { //ignored } return invite.MemberEvent.Sender!; } private async Task GetRoomNameAsync(InviteHandlerHostedService.InviteEventArgs invite) { // try get room name from invite state var name = invite.InviteData.InviteState?.Events? .FirstOrDefault(evt => evt is { Type: RoomNameEventContent.EventId, StateKey: "" })? .ContentAs()?.Name; if (!string.IsNullOrWhiteSpace(name)) return name; // try get room alias var alias = invite.InviteData.InviteState?.Events? .FirstOrDefault(evt => evt is { Type: RoomCanonicalAliasEventContent.EventId, StateKey: "" })? .ContentAs()?.Alias; if (!string.IsNullOrWhiteSpace(alias)) return alias; // try get room name via public previews try { name = await invite.Homeserver.GetRoom(invite.RoomId).GetNameOrFallbackAsync(); if (name != invite.RoomId && !string.IsNullOrWhiteSpace(name)) return name; } catch { //ignored } // fallback to room alias via public previews try { alias = (await invite.Homeserver.GetRoom(invite.RoomId).GetCanonicalAliasAsync())?.Alias; if (!string.IsNullOrWhiteSpace(alias)) return alias; } catch { //ignored } // fall back to room ID return invite.RoomId; } }