diff --git a/src/matrixeventprocessor.ts b/src/matrixeventprocessor.ts index 55061171406ef273b1b3273900aa359d7ccb8e18..01f796392b6802c87b5169897141c286f196aa8d 100644 --- a/src/matrixeventprocessor.ts +++ b/src/matrixeventprocessor.ts @@ -11,6 +11,8 @@ import * as log from "npmlog"; const MaxFileSize = 8000000; const MIN_NAME_LENGTH = 2; const MAX_NAME_LENGTH = 32; +const DISCORD_EMOJI_REGEX = /:(\w+):/g; + export class MatrixEventProcessorOpts { constructor( readonly config: DiscordBridgeConfig, @@ -45,6 +47,10 @@ export class MatrixEventProcessor { if (this.config.bridge.disableHereMention) { body = body.replace(new RegExp(`@here`, "g"), "@ here"); } + + // Handle discord custom emoji + body = this.ReplaceDiscordEmoji(body, channel.guild); + let displayName = event.sender; let avatarUrl = undefined; if (profile) { @@ -92,6 +98,23 @@ export class MatrixEventProcessor { return body; } + public ReplaceDiscordEmoji(content: string, guild: Discord.Guild): string { + let results = DISCORD_EMOJI_REGEX.exec(content); + while (results !== null) { + const emojiName = results[1]; + const emojiNameWithColons = results[0]; + + // Check if this emoji exists in the guild + const emoji = guild.emojis.find((e) => e.name === emojiName); + if (emoji) { + // Replace :a: with <:a:123ID123> + content = content.replace(emojiNameWithColons, `<${emojiNameWithColons}${emoji.id}>`); + } + results = DISCORD_EMOJI_REGEX.exec(content); + } + return content; + } + public async HandleAttachment(event: any, mxClient: any): Promise<string|Discord.FileOptions> { const hasAttachment = [ "m.image", diff --git a/src/messageprocessor.ts b/src/messageprocessor.ts index f02893357d4fd442546ed0a3beb85fae02053163..919322fbd232b33f166d814f8c5e0d4718e947f0 100644 --- a/src/messageprocessor.ts +++ b/src/messageprocessor.ts @@ -25,7 +25,6 @@ export class MessageProcessorOpts { constructor (readonly domain: string, readonly bot: DiscordBot = null) { } - } export class MessageProcessorMatrixResult { @@ -163,7 +162,9 @@ export class 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/mocks/collection.ts b/test/mocks/collection.ts index b72484108081c3e017e18fa5abee680d707e8214..c02d9c8055a072ae436ea974c57c8b81168c8d08 100644 --- a/test/mocks/collection.ts +++ b/test/mocks/collection.ts @@ -1,4 +1,6 @@ -export class MockCollection<T1, T2> extends Map { +import { Collection } from "discord.js"; + +export class MockCollection<T1, T2> extends Collection<T1, T2> { public array(): T2[] { return [...this.values()]; } diff --git a/test/mocks/emoji.ts b/test/mocks/emoji.ts new file mode 100644 index 0000000000000000000000000000000000000000..06c65350b86cd05a35754a25244c8382cc705970 --- /dev/null +++ b/test/mocks/emoji.ts @@ -0,0 +1,3 @@ +export class MockEmoji { + constructor (public id: string = "", public name = "") { } +} diff --git a/test/mocks/guild.ts b/test/mocks/guild.ts index 670d298392d9bfc0c105a39059a84f76a917083b..2c6a5657cd5477efc476c95a71ecac343b772599 100644 --- a/test/mocks/guild.ts +++ b/test/mocks/guild.ts @@ -1,10 +1,12 @@ import {MockCollection} from "./collection"; import {MockMember} from "./member"; +import {MockEmoji} from "./emoji"; import {Channel} from "discord.js"; export class MockGuild { public channels = new MockCollection<string, Channel>(); public members = new MockCollection<string, MockMember>(); + public emojis = new MockCollection<string, MockEmoji>(); public id: string; public name: string; constructor(id: string, channels: any[] = [], name: string = null) { diff --git a/test/test_matrixeventprocessor.ts b/test/test_matrixeventprocessor.ts index 07c4efe7e547e2182702ed939ed9236bfe992a98..d2ca112d634a8cc975ca3da74dbfc54bffd8e058 100644 --- a/test/test_matrixeventprocessor.ts +++ b/test/test_matrixeventprocessor.ts @@ -7,7 +7,9 @@ import * as Proxyquire from "proxyquire"; import { PresenceHandler } from "../src/presencehandler"; import { DiscordBot } from "../src/bot"; import { MockGuild } from "./mocks/guild"; +import { MockCollection } from "./mocks/collection"; import { MockMember } from "./mocks/member"; +import { MockEmoji } from "./mocks/emoji"; import {MatrixEventProcessor, MatrixEventProcessorOpts} from "../src/matrixeventprocessor"; import {DiscordBridgeConfig} from "../src/config"; import {MessageProcessor, MessageProcessorOpts} from "../src/messageprocessor"; @@ -204,6 +206,42 @@ describe("MatrixEventProcessor", () => { }, {avatar_url: "test"}, mockChannel as any); Chai.assert.equal(evt.description, "@ here Hello!"); }); + + it("Should process custom discord emojis.", () => { + const processor = createMatrixEventProcessor(false, false, true); + const mockEmoji = new MockEmoji("123", "supercake"); + const mockCollectionEmojis = new MockCollection<string, MockEmoji>(); + mockCollectionEmojis.set("123", mockEmoji); + + const mockChannelEmojis = new MockChannel("test", { + emojis: mockCollectionEmojis, + }); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "I like :supercake:", + }, + }, {avatar_url: "test"}, mockChannelEmojis as any); + Chai.assert.equal(evt.description, "I like <:supercake:123>"); + }); + + it("Should not process invalid custom discord emojis.", () => { + const processor = createMatrixEventProcessor(false, false, true); + const mockEmoji = new MockEmoji("123", "supercake"); + const mockCollectionEmojis = new MockCollection<string, MockEmoji>(); + mockCollectionEmojis.set("123", mockEmoji); + + const mockChannelEmojis = new MockChannel("test", { + emojis: mockCollectionEmojis, + }); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "I like :lamecake:", + }, + }, {avatar_url: "test"}, mockChannelEmojis as any); + Chai.assert.equal(evt.description, "I like :lamecake:"); + }); }); describe("FindMentionsInPlainBody", () => { it("processes mentioned username correctly", async () => {