"use strict";
/*
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MatrixAction = exports.ActionType = void 0;
const ircFormatting = require("../irc/formatting");
const matrix_appservice_bridge_1 = require("matrix-appservice-bridge");
const escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
const logging_1 = __importDefault(require("../logging"));
const log = (0, logging_1.default)("MatrixAction");
var ActionType;
(function (ActionType) {
    ActionType["Audio"] = "audio";
    ActionType["Command"] = "command";
    ActionType["Emote"] = "emote";
    ActionType["File"] = "file";
    ActionType["Image"] = "image";
    ActionType["Message"] = "message";
    ActionType["Notice"] = "notice";
    ActionType["Topic"] = "topic";
    ActionType["Video"] = "video";
})(ActionType = exports.ActionType || (exports.ActionType = {}));
const EVENT_TO_TYPE = {
    "m.room.message": ActionType.Message,
    "m.room.topic": ActionType.Topic,
};
const ACTION_TYPE_TO_MSGTYPE = {
    audio: undefined,
    command: undefined,
    emote: "m.emote",
    file: undefined,
    image: undefined,
    message: "m.text",
    notice: "m.notice",
    topic: undefined,
    video: undefined,
};
const MSGTYPE_TO_TYPE = {
    "m.emote": ActionType.Emote,
    "m.notice": ActionType.Notice,
    "m.image": ActionType.Image,
    "m.video": ActionType.Video,
    "m.audio": ActionType.Audio,
    "m.file": ActionType.File,
};
const PILL_MIN_LENGTH_TO_MATCH = 4;
const MAX_MATCHES = 5;
const MentionRegex = function (matcher) {
    const WORD_BOUNDARY = "^|:|#|```|\\s|'|<|>|;|&|$|,";
    return new RegExp(`(${WORD_BOUNDARY})(@?(${matcher}))(?=${WORD_BOUNDARY})`, "igm");
};
class MatrixAction {
    type;
    text;
    htmlText;
    ts;
    replyEvent;
    constructor(type, text = null, htmlText = null, ts = 0, replyEvent) {
        this.type = type;
        this.text = text;
        this.htmlText = htmlText;
        this.ts = ts;
        this.replyEvent = replyEvent;
    }
    get msgType() {
        return ACTION_TYPE_TO_MSGTYPE[this.type];
    }
    async formatMentions(nickUserIdMap, intent) {
        if (!this.text) {
            return;
        }
        const nicks = [...nickUserIdMap.keys()];
        const regexString = `(${nicks.map((value) => (0, escape_string_regexp_1.default)(value)).join("|")})`;
        const usersRegex = MentionRegex(regexString);
        const matched = new Set(); // lowercased nicknames we have matched already.
        let match;
        for (let i = 0; i < MAX_MATCHES && (match = usersRegex.exec(this.text)) !== null; i++) {
            let matchName = match[2];
            // Deliberately have a minimum length to match on,
            // so we don't match smaller nicks accidentally.
            if (matchName.length < PILL_MIN_LENGTH_TO_MATCH || matched.has(matchName.toLowerCase())) {
                continue;
            }
            let userId = nickUserIdMap.get(matchName);
            if (userId === undefined) {
                // We might need to search case-insensitive.
                const nick = nicks.find((n) => n.toLowerCase() === matchName.toLowerCase());
                if (nick === undefined) {
                    continue;
                }
                userId = nickUserIdMap.get(nick);
                matchName = nick;
            }
            if (!userId) {
                continue;
            }
            // If this message is not HTML, we should make it so.
            if (!this.htmlText) {
                // This looks scary and unsafe, but further down we check
                // if `text` contains any HTML and escape + set `htmlText` appropriately.
                this.htmlText = this.text;
            }
            userId = ircFormatting.escapeHtmlChars(userId);
            /* Due to how Element and friends do push notifications,
            we need the plain text to match something.*/
            let identifier;
            try {
                identifier = (await intent.getProfileInfo(userId, 'displayname', true)).displayname || undefined;
            }
            catch (e) {
                // This shouldn't happen, but let's not fail to match if so.
            }
            if (identifier === undefined) {
                // Fallback to userid.
                identifier = userId.substring(1, userId.indexOf(":"));
            }
            const regex = MentionRegex((0, escape_string_regexp_1.default)(matchName));
            this.htmlText = this.htmlText.replace(regex, `$1<a href="https://matrix.to/#/${userId}">` +
                `${ircFormatting.escapeHtmlChars(identifier)}</a>`);
            this.text = this.text.replace(regex, `$1${identifier}`);
            // Don't match this name twice, we've already replaced all entries.
            matched.add(matchName.toLowerCase());
        }
    }
    static fromEvent(event, mediaUrl, filename) {
        event.content = event.content || {};
        let type = EVENT_TO_TYPE[event.type] || "message"; // mx event type to action type
        let text = event.content.body;
        let htmlText = null;
        if (event.type === "m.room.topic") {
            text = event.content.topic;
        }
        else if (event.type === "m.room.message") {
            if (event.content.msgtype === 'm.text' && event.content.body?.startsWith('!irc ')) {
                // This might be a command
                type = ActionType.Command;
                return new MatrixAction(type, text, null, event.origin_server_ts, event.event_id);
            }
            if (event.content.format === "org.matrix.custom.html") {
                htmlText = event.content.formatted_body;
            }
            if (MSGTYPE_TO_TYPE[event.content.msgtype]) {
                type = MSGTYPE_TO_TYPE[event.content.msgtype];
            }
            const isFile = ["m.image", "m.file", "m.video", "m.audio"].includes(event.content.msgtype);
            if (isFile && event.content.url) {
                let fileSize = "";
                if (event.content.info && event.content.info.size &&
                    typeof event.content.info.size === "number") {
                    fileSize = "(" + Math.round(event.content.info.size / 1024) + "KiB)";
                }
                let url = matrix_appservice_bridge_1.ContentRepo.getHttpUriForMxc(mediaUrl, event.content.url);
                if (!filename && event.content.body && /\S*\.[\w\d]{2,4}$/.test(event.content.body)) {
                    // Add filename to url if body is a filename.
                    filename = event.content.body;
                }
                if (filename) {
                    url += `/${encodeURIComponent(filename)}`;
                    text = `${fileSize} < ${url} >`;
                }
                else {
                    fileSize = fileSize ? ` ${fileSize}` : "";
                    // If not a filename, print the body
                    text = `${event.content.body}${fileSize} < ${url} >`;
                }
            }
        }
        return new MatrixAction(type, text, htmlText, event.origin_server_ts);
    }
    static fromIrcAction(ircAction) {
        switch (ircAction.type) {
            case "message":
            case "emote":
            case "notice": {
                const htmlText = ircFormatting.ircToHtml(ircAction.text);
                return new MatrixAction(ircAction.type, ircFormatting.stripIrcFormatting(ircAction.text), 
                // only set HTML text if we think there is HTML, else the bridge
                // will send everything as HTML and never text only.
                ircAction.text !== htmlText ? htmlText : undefined);
            }
            case "topic":
                return new MatrixAction(ActionType.Topic, ircAction.text);
            default:
                log.error("MatrixAction.fromIrcAction: Unknown action: %s", ircAction.type);
                return null;
        }
    }
}
exports.MatrixAction = MatrixAction;
//# sourceMappingURL=MatrixAction.js.map