about summary refs log tree commit diff
path: root/MatrixAntiDmSpam/InviteHandler.cs
blob: 2a5d17bdfa34ef5b46040f518b9cf2de1f992459 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System.Text.Json;
using ArcaneLibs;
using ArcaneLibs.Extensions;
using LibMatrix.EventTypes.Spec.State.RoomInfo;
using LibMatrix.Helpers;
using LibMatrix.Utilities.Bot.Services;

namespace MatrixAntiDmSpam;

public class InviteHandler(ILogger<InviteHandler> logger, AntiDmSpamConfiguration config, InviteStore inviteStore) : InviteHandlerHostedService.IInviteHandler {
    public async Task HandleInviteAsync(InviteHandlerHostedService.InviteEventArgs invite) {
        // logger.LogInformation("Received invite to room {}", invite.RoomId);
        await LogInvite(invite);
        await inviteStore.AddInviteAsync(invite);
    }

    private async Task LogInvite(InviteHandlerHostedService.InviteEventArgs invite) {
        if (string.IsNullOrWhiteSpace(config.LogRoom)) return;
        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<string, JsonElement>
        message.AdditionalData!["gay.rory.invite_logger.invite_data_uri"] = JsonDocument.Parse($"\"{inviteDataFileUri}\"").RootElement;

        await logRoom.SendMessageEventAsync(message);

        if (config.LogInviteDataAsFile) {
            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<string> GetInviterNameAsync(InviteHandlerHostedService.InviteEventArgs invite) {
        var name = invite.InviteData.InviteState?.Events?
            .FirstOrDefault(evt => evt is { Type: RoomMemberEventContent.EventId } && evt.StateKey == invite.MemberEvent.Sender)?
            .ContentAs<RoomMemberEventContent>()?.DisplayName;

        if (!string.IsNullOrWhiteSpace(name))
            return name;

        try {
            await invite.Homeserver.GetProfileAsync(invite.MemberEvent.Sender!);
        }
        catch {
            //ignored
        }

        return invite.MemberEvent.Sender!;
    }

    private async Task<string> 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<RoomNameEventContent>()?.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<RoomCanonicalAliasEventContent>()?.Alias;

        if (!string.IsNullOrWhiteSpace(alias))
            return alias;

        // try get room name via public previews
        try {
#pragma warning disable CS0618 // Type or member is obsolete
            name = await invite.Homeserver.GetRoom(invite.RoomId).GetNameOrFallbackAsync();
#pragma warning restore CS0618 // Type or member is obsolete
            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;
    }
}