diff --git a/src/bot.ts b/src/bot.ts index 7cf5ade82eb90ca54fcd4da7433b886ad2137697..c6c05a8388fd3e5b6580fbfcf08205d0b3abc3b2 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,6 +1,7 @@ import { DiscordBridgeConfig } from "./config"; import { DiscordClientFactory } from "./clientfactory"; import { DiscordStore } from "./store"; +import { DbGuildEmoji } from "./db/dbdataemoji"; import { MatrixUser, RemoteUser, Bridge, Entry } from "matrix-appservice-bridge"; import { Util } from "./util"; import { MessageProcessor, MessageProcessorOpts } from "./messageprocessor"; @@ -36,6 +37,7 @@ export class DiscordBot { this.clientFactory = new DiscordClientFactory(store, config.auth); this.msgProcessor = new MessageProcessor( new MessageProcessorOpts(this.config.bridge.domain), + this, ); } @@ -243,6 +245,25 @@ export class DiscordBot { }); } + public async GetGuildEmoji(guild: Discord.Guild, id: string): Promise<string> { + const dbEmoji: DbGuildEmoji = await this.store.Get(DbGuildEmoji, {emoji_id: id}); + if (!dbEmoji.Result) { + // Fetch the emoji + if (!guild.emojis.has(id)) { + throw new Error("The guild does not contain the emoji"); + } + const emoji: Discord.Emoji = guild.emojis.get(id); + const intent = this.bridge.getIntent(); + const mxcUrl = (await Util.UploadContentFromUrl(emoji.url, intent, emoji.name)).mxcUrl; + dbEmoji.EmojiId = emoji.id; + dbEmoji.GuildId = guild.id; + dbEmoji.Name = emoji.name; + dbEmoji.MxcUrl = mxcUrl; + await this.store.Insert(dbEmoji); + } + return dbEmoji.MxcUrl; + } + private GetFilenameForMediaEvent(content): string { if (content.body) { if (path.extname(content.body) !== "") { @@ -500,14 +521,15 @@ export class DiscordBot { }); if (msg.content !== null && msg.content !== "") { // Replace mentions. - const result = this.msgProcessor.FormatDiscordMessage(msg); - rooms.forEach((room) => { - intent.sendMessage(room, { - body: result.body, - msgtype: "m.text", - formatted_body: result.formattedBody, - format: "org.matrix.custom.html", - }); + this.msgProcessor.FormatDiscordMessage(msg).then((result) => { + rooms.forEach((room) => { + intent.sendMessage(room, { + body: result.body, + msgtype: "m.text", + formatted_body: result.formattedBody, + format: "org.matrix.custom.html", + }); + }); }); } }).catch((err) => { diff --git a/src/messageprocessor.ts b/src/messageprocessor.ts index 5f78fe0e26ed369b2e0eb0ce2d2b91ea8326e689..8d5639dc6c3f211dff4f8a3069227800fb2f37bc 100644 --- a/src/messageprocessor.ts +++ b/src/messageprocessor.ts @@ -1,5 +1,7 @@ import * as Discord from "discord.js"; import * as marked from "marked"; +import * as log from "npmlog"; +import { DiscordBot } from "./bot"; const USER_REGEX = /<@!?([0-9]*)>/g; const CHANNEL_REGEX = /<#?([0-9]*)>/g; @@ -21,18 +23,19 @@ export class MessageProcessorMatrixResult { export class MessageProcessor { private readonly opts: MessageProcessorOpts; - - constructor (opts: MessageProcessorOpts) { + private readonly bot: DiscordBot; + constructor (opts: MessageProcessorOpts, bot: DiscordBot) { this.opts = opts; + this.bot = bot; } - public FormatDiscordMessage(msg: Discord.Message): MessageProcessorMatrixResult { + public async FormatDiscordMessage(msg: Discord.Message): Promise<MessageProcessorMatrixResult> { const result = new MessageProcessorMatrixResult(); // Replace Users let content = msg.content; content = this.ReplaceMembers(content, msg); content = this.ReplaceChannels(content, msg); - // content = this.ReplaceEmoji(content, msg); + content = await this.ReplaceEmoji(content, msg); // Replace channels result.body = content; result.formattedBody = marked(content); @@ -65,16 +68,20 @@ export class MessageProcessor { return content; } - public ReplaceEmoji(content: string, msg: Discord.Message): string { - // let results = EMOJI_REGEX.exec(content); - // while (results !== null) { - // const id = results[1]; - // // const channel = msg.guild.channels.get(id); - // // const roomId = `#_discord_${msg.guild.id}_${id}:${this.opts.domain}`; - // // const channelStr = channel ? "#" + channel.name : "#" + id; - // // content = content.replace(results[0], `[${channelStr}](${MATRIX_TO_LINK}${roomId})`); - // results = EMOJI_REGEX.exec(content); - // } + public async ReplaceEmoji(content: string, msg: Discord.Message): Promise<string> { + let results = EMOJI_REGEX.exec(content); + while (results !== null) { + const id = results[1]; + try { + const mxcUrl = await this.bot.GetGuildEmoji(msg.guild, id); + content = content.replace(results[0], ``); + } catch (ex) { + log.warn("MessageProcessor", + `Could not insert emoji ${id} for msg ${msg.id} in guild ${msg.guild.id}: ${ex}`, + ); + } + results = EMOJI_REGEX.exec(content); + } return content; } } diff --git a/test/test_messageprocessor.ts b/test/test_messageprocessor.ts index d3689b1dfd549fa2722bec2052db5a1c9c2e71c6..fdffdb4b8aab9969fdc9b757f23ab427aff72dcd 100644 --- a/test/test_messageprocessor.ts +++ b/test/test_messageprocessor.ts @@ -2,8 +2,11 @@ import * as Chai from "chai"; import * as ChaiAsPromised from "chai-as-promised"; import * as log from "npmlog"; import * as Discord from "discord.js"; +import * as Proxyquire from "proxyquire"; + // import * as Proxyquire from "proxyquire"; import { MessageProcessor, MessageProcessorOpts } from "../src/messageprocessor"; +import { DiscordBot } from "../src/bot"; import { MockGuild } from "./mocks/guild"; import { MockMember } from "./mocks/member"; @@ -12,34 +15,43 @@ const expect = Chai.expect; log.level = "silly"; // const assert = Chai.assert; +const bot = { + GetGuildEmoji: (guild: Discord.Guild, id: string): Promise<string> => { + if (id === "3333333") { + return Promise.resolve("mxc://image"); + } else { + throw new Error("Emoji not found"); + } + }, +}; describe("MessageProcessor", () => { describe("init", () => { it("constructor", () => { - const mp = new MessageProcessor(new MessageProcessorOpts("localhost")); + const mp = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); }); }); describe("FormatDiscordMessage", () => { - it("processes plain text messages correctly", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + it("processes plain text messages correctly", async () => { + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const msg = new Discord.Message(null, null, null); msg.content = "Hello World!"; - const result = processor.FormatDiscordMessage(msg); + const result = await processor.FormatDiscordMessage(msg); Chai.assert(result.body, "Hello World!"); Chai.assert(result.formattedBody, "Hello World!"); }); - it("processes markdown messages correctly.", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + it("processes markdown messages correctly.", async () => { + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const msg = new Discord.Message(null, null, null); msg.content = "Hello *World*!"; - const result = processor.FormatDiscordMessage(msg); + const result = await processor.FormatDiscordMessage(msg); Chai.assert.equal(result.body, "Hello *World*!"); Chai.assert.equal(result.formattedBody, "<p>Hello <em>World</em>!</p>\n"); }); }); describe("ReplaceMembers", () => { it("processes members missing from the guild correctly", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const guild: any = new MockGuild("123", []); const channel = new Discord.TextChannel(guild, null); const msg = new Discord.Message(channel, null, null); @@ -48,7 +60,7 @@ describe("MessageProcessor", () => { Chai.assert.equal(content, "Hello @_discord_12345:localhost"); }); it("processes members with usernames correctly", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const guild: any = new MockGuild("123", []); guild._mockAddMember(new MockMember("12345", "TestUsername")); const channel = new Discord.TextChannel(guild, null); @@ -60,7 +72,7 @@ describe("MessageProcessor", () => { }); describe("ReplaceChannels", () => { it("processes unknown channel correctly", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const guild: any = new MockGuild("123", []); const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"}); const msg = new Discord.Message(channel, null, null); @@ -69,7 +81,7 @@ describe("MessageProcessor", () => { Chai.assert.equal(content, "Hello [#123456789](https://matrix.to/#/#_discord_123_123456789:localhost)"); }); it("processes channels correctly", () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost")); + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); const guild: any = new MockGuild("123", []); const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"}); guild.channels.set("456", channel); @@ -79,4 +91,25 @@ describe("MessageProcessor", () => { Chai.assert.equal(content, "Hello [#TestChannel](https://matrix.to/#/#_discord_123_456:localhost)"); }); }); + describe("ReplaceEmoji", () => { + it("processes unknown emoji correctly", async () => { + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); + const guild: any = new MockGuild("123", []); + const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"}); + const msg = new Discord.Message(channel, null, null); + let content = "Hello <:hello:123456789>"; + content = await processor.ReplaceEmoji(content, msg); + Chai.assert.equal(content, "Hello <:hello:123456789>"); + }); + it("processes emoji correctly", async () => { + const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); + const guild: any = new MockGuild("123", []); + const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"}); + guild.channels.set("456", channel); + const msg = new Discord.Message(channel, null, null); + let content = "Hello <:hello:3333333>"; + content = await processor.ReplaceEmoji(content, msg); + Chai.assert.equal(content, "Hello "); + }); + }); });