~cadence/matrix-appservice-discord

d139d819b7e196e8fca38a05af013e515e428da0 — Cadence Ember 6 months ago fcfda7f
Bridge discord replies to matrix
4 files changed, 102 insertions(+), 26 deletions(-)

M README.md
M patch_node_modules.fish
M src/bot.ts
M src/discordmessageprocessor.ts
M README.md => README.md +1 -0
@@ 6,6 6,7 @@ This is a fork of [Half-Shot's matrix-appservice-discord](https://github.com/Hal

- [<-->] Spoiler text formatting
- [D->M] Edits are bridged as edits
- [D->M] Replies are implemented
- [M->D] Replies are vastly improved, and they mention the person that was replied to
- [D->M] Formatting of embeds is vastly improved
- [M->D] Writing plaintext @mentions will be bridged as mentions

M patch_node_modules.fish => patch_node_modules.fish +51 -23
@@ 2,31 2,59 @@

cd (dirname (status current-filename))

# --- the patch function

function patch_file
	set -l filename $argv[1]
	set -l method $argv[2]
	set -l key $argv[3]
	set -l find $argv[4]
	set -l replace $argv[5]

	if grep -F $key $filename >/dev/null
		echo "$filename: already patched"
		return
	end

	switch $method
		case insert
			sed -i "s/$find/&$replace/" $filename; or exit
		case '*'
			echo "$filename: unknown patch method '$method'" >&2
			exit 2
	end

	echo "$filename: patched"
    set -l filename $argv[1]
    set -l method $argv[2]
    set -l key $argv[3]
    set -l find $argv[4]
    set -l replace $argv[5]

    if grep -F $key $filename >/dev/null
        echo "$filename: already patched"
        return
    end

    switch $method
        case insert
            sed -i "s/$find/&$replace/" $filename; or exit
        case '*'
            echo "$filename: unknown patch method '$method'" >&2
            exit 2
    end

    echo "$filename: patched"
end

# --- patch matrix-js-sdk to work after a recent synapse version

set key 'data.type = "m.login.application_service";'
patch_file node_modules/matrix-js-sdk/lib/base-apis.js insert \
	$key \
	'prototype.registerRequest =.*' \
	"\n\n    $key\n"
    $key \
    'prototype.registerRequest =.*' \
    "\n\n    $key\n"

# --- patch discord.js to support message references

set key 'this.messageReference = {'
set replace "\n
    \/*
     * Message reference fields
     * @type {{guild_id: Snowflake, channel_id: Snowflake, message_id: Snowflake, message: any}}
     *\/
    $key
      ...data.message_reference,
      message: data.referenced_message
    };"
set replace (string replace -a \n '\n' $replace)
patch_file node_modules/discord.js/src/structures/Message.js insert \
    $key \
    'this.hit = typeof data.hit.*' \
    $replace

set key 'public messageReference:'
patch_file node_modules/discord.js/typings/index.d.ts insert \
    $key \
    'public mentions: MessageMentions;' \
    "\n\t\t$key {guild_id: Snowflake, channel_id: Snowflake, message_id: Snowflake, message: any};"

M src/bot.ts => src/bot.ts +11 -3
@@ 88,7 88,7 @@ export class DiscordBot {
        private botUserId: string,
        private config: DiscordBridgeConfig,
        private bridge: Bridge,
        private store: DiscordStore,
        public store: DiscordStore,
    ) {

        // create handlers


@@ 846,12 846,20 @@ export class DiscordBot {
                return;
            }
            await Util.AsyncForEach(rooms, async (room) => {
                const trySend = async () => intent.sendMessage(room, {
                const sendData = {
                    body: result.body,
                    format: "org.matrix.custom.html",
                    formatted_body: result.formattedBody,
                    msgtype: result.msgtype,
                });
                };
                if (result.replyTo) {
                    sendData["m.relates_to"] = {
                        "m.in_reply_to": {
                            "event_id": result.replyTo
                        }
                    };
                }
                const trySend = async () => intent.sendMessage(room, sendData);
                const afterSend = async (re) => {
                    this.lastEventIds[room] = re.event_id;
                    const evt = new DbEvent();

M src/discordmessageprocessor.ts => src/discordmessageprocessor.ts +39 -0
@@ 20,6 20,7 @@ import { DiscordBot } from "./bot";
import * as escapeHtml from "escape-html";
import { Util } from "./util";
import { Bridge } from "matrix-appservice-bridge";
import { DbEvent } from "../src/db/dbdataevent";

import { Log } from "./log";
const log = new Log("DiscordMessageProcessor");


@@ 48,6 49,7 @@ export class DiscordMessageProcessorResult {
    public formattedBody: string;
    public body: string;
    public msgtype: string;
    public replyTo?: string;
}

interface IDiscordNode {


@@ 100,6 102,43 @@ export class DiscordMessageProcessor {
        result.body = content;
        result.formattedBody = contentPostmark;
        result.msgtype = "m.text";

        // figure out whether it's a reply
        if (msg.messageReference && msg.messageReference.message_id) {
            // if there is content, insert the content into the matrix message too. this legacy display is used by element android
            if (msg.messageReference.message && msg.messageReference.message.content && msg.messageReference.message.author && msg.messageReference.message.author.username) {
                // > <@she_who_brings_destruction:cadence.moe> https://twitter.com/NutDivide/status/1484418483018354688\n\ncum
                // <mx-reply><blockquote><a href=\"https://matrix.to/#/!JVDXYBIKlkpdnIfHuh:cadence.moe/$xHmIH-oQwva6kHg4drvUVbCy-yrut1u_4d3gTeZbY90?via=cadence.moe\">In reply to</a> <a href=\"https://matrix.to/#/@she_who_brings_destruction:cadence.moe\">@she_who_brings_destruction:cadence.moe</a><br>https://twitter.com/NutDivide/status/1484418483018354688</blockquote></mx-reply>cum
                const quotedUsername = msg.messageReference.message.author.username
                const quotedContent = msg.messageReference.message.content
                // body
                let block = "<quotedUsername>: " + quotedContent
                let reQuoted = block.split("\n").map(line => "> " + line).join("\n")
                result.body = reQuoted + "\n\n" + result.body
                // formatted body
                const quotedContentPostmark = markdown.toHTML(quotedContent, {
                    discordCallback: this.getDiscordParseCallbacksHTML(msg),
                });
                const escapedUsername = markdown.toHTML(quotedUsername, {
                    discordCallback: this.getDiscordParseCallbacks(msg),
                    discordOnly: true,
                    escapeHTML: true,
                });
                result.formattedBody = `<mx-reply><blockquote><em>In reply to ${escapedUsername}</em><br>${quotedContentPostmark}</blockquote></mx-reply>` + result.formattedBody
            }
            // try to look up the message, and set the m.reply_to field (this makes it display on element desktop and fluffychat)
            const storeEvent = await this.opts.bot!.store.Get(DbEvent, {discord_id: msg.messageReference.message_id});
            let matrixIds: string[] | null = null;
            if (storeEvent && storeEvent.Result) {
                while (storeEvent.Next()) {
                    matrixIds = storeEvent.MatrixId.split(";");
                }
            }
            if (matrixIds) {
                result.replyTo = matrixIds[0]
            }
        }

        return result;
    }