diff --git a/src/config.ts b/src/config.ts index 12b8e7382b5042d9dd5eeb315d29d07cb6479228..b836c2bacaa593acc5ffea89dec2a9af0dcfd567 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,11 +1,11 @@ /** Type annotations for config/config.schema.yaml */ export class DiscordBridgeConfig { - public bridge: DiscordBridgeConfigBridge; - public auth: DiscordBridgeConfigAuth; - public logging: DiscordBridgeConfigLogging; - public database: DiscordBridgeConfigDatabase; - public room: DiscordBridgeConfigRoom; + public bridge: DiscordBridgeConfigBridge = new DiscordBridgeConfigBridge(); + public auth: DiscordBridgeConfigAuth = new DiscordBridgeConfigAuth(); + public logging: DiscordBridgeConfigLogging = new DiscordBridgeConfigLogging(); + public database: DiscordBridgeConfigDatabase = new DiscordBridgeConfigDatabase(); + public room: DiscordBridgeConfigRoom = new DiscordBridgeConfigRoom(); } class DiscordBridgeConfigBridge { @@ -17,6 +17,8 @@ class DiscordBridgeConfigBridge { public disableDiscordMentions: boolean; public disableDeletionForwarding: boolean; public enableSelfServiceBridging: boolean; + public disableEveryoneMention: boolean = false; + public disableHereMention: boolean = false; } class DiscordBridgeConfigDatabase { diff --git a/src/matrixeventprocessor.ts b/src/matrixeventprocessor.ts index e07f922e12fe26a7add0f6ed6537c2f41452264b..f63b76b27549602869c4a7e2ee1b7a30f483fcb9 100644 --- a/src/matrixeventprocessor.ts +++ b/src/matrixeventprocessor.ts @@ -2,12 +2,13 @@ import * as Discord from "discord.js"; import {MessageProcessorOpts, MessageProcessor} from "./messageprocessor"; import {DiscordBot} from "./bot"; import {DiscordBridgeConfig} from "./config"; +import * as escapeStringRegexp from "escape-string-regexp"; export class MatrixEventProcessorOpts { constructor( readonly config: DiscordBridgeConfig, readonly bridge: any, - readonly msgProcessor: MessageProcessor) { + ) { } } @@ -15,20 +16,29 @@ export class MatrixEventProcessorOpts { export class MatrixEventProcessor { private config: DiscordBridgeConfig; private bridge: any; - private msgProcessor: MessageProcessor; constructor (opts: MatrixEventProcessorOpts) { this.config = opts.config; - this.msgProcessor = opts.msgProcessor; this.bridge = opts.bridge; } - public EventToEmbed(event: any, profile: any, channel: Discord.TextChannel): Discord.RichEmbed { - const body = this.config.bridge.disableDiscordMentions ? event.content.body : - this.msgProcessor.FindMentionsInPlainBody( + public EventToEmbed(event: any, profile: any|null, channel: Discord.TextChannel): Discord.RichEmbed { + let body = this.config.bridge.disableDiscordMentions ? event.content.body : + this.FindMentionsInPlainBody( event.content.body, channel.members.array(), ); + + // Replace @everyone + if (this.config.bridge.disableEveryoneMention) { + body = body.replace(new RegExp(`@everyone`, "g"), "@ everyone"); + } + + // Replace @here + if (this.config.bridge.disableHereMention) { + body = body.replace(new RegExp(`@here`, "g"), "@ here"); + } + if (profile) { profile.displayname = profile.displayname || event.sender; if (profile.avatar_url) { @@ -54,7 +64,24 @@ export class MatrixEventProcessor { }); } return new Discord.RichEmbed({ + author: { + name: event.sender, + url: `https://matrix.to/#/${event.sender}`, + }, description: body, }); } + + public FindMentionsInPlainBody(body: string, members: Discord.GuildMember[]): string { + for (const member of members) { + const matcher = escapeStringRegexp(member.user.username + "#" + member.user.discriminator) + "|" + + escapeStringRegexp(member.displayName); + body = body.replace( + new RegExp( + `\\b(${matcher})(?=\\b)` + , "mig"), `<@!${member.id}>`, + ); + } + return body; + } } diff --git a/src/messageprocessor.ts b/src/messageprocessor.ts index 7be2a99d80891e0c25f5a793e55f9791d12bed62..cac98a036261a07861e7c3d27b641e27ecff5e0f 100644 --- a/src/messageprocessor.ts +++ b/src/messageprocessor.ts @@ -2,7 +2,6 @@ import * as Discord from "discord.js"; import * as marked from "marked"; import * as log from "npmlog"; import { DiscordBot } from "./bot"; -import * as escapeStringRegexp from "escape-string-regexp"; import * as escapeHtml from "escape-html"; const USER_REGEX = /<@!?([0-9]*)>/g; @@ -39,8 +38,9 @@ export class MessageProcessor { // Backwards compat if (bot != null) { this.opts = new MessageProcessorOpts(opts.domain, bot); + } else { + this.opts = opts; } - this.opts = opts; } public async FormatDiscordMessage(msg: Discord.Message): Promise<MessageProcessorMatrixResult> { @@ -178,17 +178,4 @@ export class MessageProcessor { } return content; } - - public FindMentionsInPlainBody(body: string, members: Discord.GuildMember[]): string { - for (const member of members) { - const matcher = escapeStringRegexp(member.user.username + "#" + member.user.discriminator) + "|" + - escapeStringRegexp(member.displayName); - body = body.replace( - new RegExp( - `\\b(${matcher})(?=\\b)` - , "mig"), `<@!${member.id}>`, - ); - } - return body; - } } diff --git a/test/mocks/channel.ts b/test/mocks/channel.ts new file mode 100644 index 0000000000000000000000000000000000000000..c347cd5878fd2069e53bc9586ea0ed6f290270a6 --- /dev/null +++ b/test/mocks/channel.ts @@ -0,0 +1,9 @@ +import {MockUser} from "./user"; +import * as Discord from "discord.js"; +import {MockMember} from "./member"; +import {MockCollection} from "./collection"; + +//Mocking TextChannel +export class MockChannel { + public members = new MockCollection<string, MockMember>(); +} diff --git a/test/mocks/guild.ts b/test/mocks/guild.ts index d79fd8823165274d9679c7c227b6ecf400095a55..30c85a23decfc45a89aaa5903a8ded6896cee95a 100644 --- a/test/mocks/guild.ts +++ b/test/mocks/guild.ts @@ -1,8 +1,9 @@ import {MockCollection} from "./collection"; import {MockMember} from "./member"; +import {Channel} from "discord.js"; export class MockGuild { - public channels = new MockCollection<string, any>(); + public channels = new MockCollection<string, Channel>(); public members = new MockCollection<string, MockMember>(); public id: string; constructor(id: string, channels: any[]) { diff --git a/test/mocks/member.ts b/test/mocks/member.ts index b614c9650032effe5f9b855550bcaddace0942f4..60227bf68e47f3d4eb124d65c013b4837d6c9411 100644 --- a/test/mocks/member.ts +++ b/test/mocks/member.ts @@ -5,10 +5,12 @@ export class MockMember { public id = ""; public presence: Discord.Presence; public user: MockUser; + public displayName: string; constructor(id: string, username: string) { this.id = id; this.presence = new Discord.Presence({}); this.user = new MockUser(this.id, username); + this.displayName = username; } public MockSetPresence(presence: Discord.Presence) { diff --git a/test/test_matrixeventprocessor.ts b/test/test_matrixeventprocessor.ts index 15013ec401e15dec2e83a02afa84b1bf34df1ac1..c63cc27fd4c630b8fc4f0c05331e897eb1f914b0 100644 --- a/test/test_matrixeventprocessor.ts +++ b/test/test_matrixeventprocessor.ts @@ -12,6 +12,7 @@ import { MockMember } from "./mocks/member"; import {MatrixEventProcessor, MatrixEventProcessorOpts} from "../src/matrixeventprocessor"; import {DiscordBridgeConfig} from "../src/config"; import {MessageProcessor, MessageProcessorOpts} from "../src/messageprocessor"; +import {MockChannel} from "./mocks/channel"; Chai.use(ChaiAsPromised); const expect = Chai.expect; @@ -28,28 +29,37 @@ const bot = { }, }; -function createMatrixEventProcessor(disableMentions: boolean = false): MatrixEventProcessor { +function createMatrixEventProcessor(disableMentions: boolean = false, disableEveryone = false, disableHere = false): MatrixEventProcessor { const bot = { - } + }; const bridge = { - + getClientFactory: () => { + return { + getClientAs: () => { + return { + mxcUrlToHttp: () => { + return "https://madeup.com"; + }, + }; + }, + }; + }, } const config = new DiscordBridgeConfig(); config.bridge.disableDiscordMentions = disableMentions; - const messageProcessor = new MessageProcessor(new MessageProcessorOpts( - "localhost", - <DiscordBot> bot, - )); + config.bridge.disableEveryoneMention = disableEveryone; + config.bridge.disableHereMention = disableHere; return new MatrixEventProcessor( new MatrixEventProcessorOpts( config, bridge, - messageProcessor, )); } +const mockChannel = new MockChannel(); +mockChannel.members.set("12345", new MockMember("12345", "testuser2")); -describe("MatrixEventPrcoessor", () => { +describe("MatrixEventProcessor", () => { describe("EventToEmbed", () => { it("Should contain a profile.", () => { const processor = createMatrixEventProcessor(); @@ -61,39 +71,170 @@ describe("MatrixEventPrcoessor", () => { }, { displayname: "Test User", avatar_url: "mxc://localhost/avatarurl", - }); - }); - - it("Should not contain a profile if one does not exist.", () => { - + }, mockChannel as any); + Chai.assert.equal(evt.author.name, "Test User"); + Chai.assert.equal(evt.author.icon_url, "https://madeup.com"); + Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); it("Should should contain the users displayname if it exists.", () => { - + const processor = createMatrixEventProcessor(); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "testcontent", + }, + }, { + displayname: "Test User"}, mockChannel as any); + Chai.assert.equal(evt.author.name, "Test User"); + Chai.assert.isUndefined(evt.author.icon_url); + Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); it("Should should contain the users userid if the displayname is not set.", () => { - + const processor = createMatrixEventProcessor(); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "testcontent", + }, + }, null, mockChannel as any); + Chai.assert.equal(evt.author.name, "@test:localhost"); + Chai.assert.isUndefined(evt.author.icon_url); + Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); it("Should should contain the users avatar if it exists.", () => { - - }); - - it("Should should contain the users userid if the avatar is not set.", () => { - + const processor = createMatrixEventProcessor(); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "testcontent", + }, + }, {avatar_url: "test"}, mockChannel as any); + Chai.assert.equal(evt.author.name, "@test:localhost"); + Chai.assert.equal(evt.author.icon_url, "https://madeup.com"); + Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); it("Should enable mentions if configured.", () => { - + const processor = createMatrixEventProcessor(); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "@testuser2 Hello!", + }, + }, {avatar_url: "test"}, mockChannel as any); + Chai.assert.equal(evt.description, "@<@!12345> Hello!"); }); it("Should disable mentions if configured.", () => { - + const processor = createMatrixEventProcessor(true); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "@testuser2 Hello!", + }, + }, {avatar_url: "test"}, mockChannel as any); + Chai.assert.equal(evt.description, "@testuser2 Hello!"); }); it("Should remove everyone mentions if configured.", () => { + const processor = createMatrixEventProcessor(false, true); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "@everyone Hello!", + }, + }, {avatar_url: "test"}, mockChannel as any); + Chai.assert.equal(evt.description, "@ everyone Hello!"); + }); + it("Should remove here mentions if configured.", () => { + const processor = createMatrixEventProcessor(false, false, true); + const evt = processor.EventToEmbed({ + sender: "@test:localhost", + content: { + body: "@here Hello!", + }, + }, {avatar_url: "test"}, mockChannel as any); + Chai.assert.equal(evt.description, "@ here Hello!"); }); }); -} + describe("FindMentionsInPlainBody", () => { + it("processes mentioned username correctly", async () => { + const processor = createMatrixEventProcessor(); + const guild: any = new MockGuild("123", []); + const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { + user: { + username: "TestUsername", + id: "12345", + discriminator: "54321", + }, + })]; + Chai.assert.equal( + processor.FindMentionsInPlainBody("Hello TestUsername", members), + "Hello <@!12345>", + ); + Chai.assert.equal( + processor.FindMentionsInPlainBody("Hello TestUsername#54321", members), + "Hello <@!12345>", + ); + }); + it("processes mentioned nickname correctly", async () => { + const processor = createMatrixEventProcessor(); + const guild: any = new MockGuild("123", []); + const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { + nick: "Test", + user: { + username: "Test", + id: "54321", + }, + }), new Discord.GuildMember(guild, { + nick: "TestNickname", + user: { + username: "TestUsername", + id: "12345", + }, + })]; + Chai.assert.equal(processor.FindMentionsInPlainBody("Hello TestNickname", members), "Hello <@!12345>"); + Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname: Hello", members), "<@!12345>: Hello"); + Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname, Hello", members), "<@!12345>, Hello"); + Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname Hello", members), "<@!12345> Hello"); + Chai.assert.equal(processor.FindMentionsInPlainBody("testNicKName Hello", members), "<@!12345> Hello"); + Chai.assert.equal( + processor.FindMentionsInPlainBody("I wish TestNickname was here", members), + "I wish <@!12345> was here", + ); + Chai.assert.equal( + processor.FindMentionsInPlainBody("I wish TestNickname was here, TestNickname is cool", members), + "I wish <@!12345> was here, <@!12345> is cool", + ); + Chai.assert.equal( + processor.FindMentionsInPlainBody("TestNickname was here with Test", members), + "<@!12345> was here with <@!54321>", + ); + }); + it("processes non-mentions correctly", async () => { + const processor = createMatrixEventProcessor(); + const guild: any = new MockGuild("123", []); + const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { + nick: "that", + user: { + username: "TestUsername", + id: "12345", + }, + }), + new Discord.GuildMember(guild, { + nick: "testingstring", + user: { + username: "that", + id: "12345", + }, + })]; + const msg = "Welcome thatman"; + const content = processor.FindMentionsInPlainBody(msg, members); + Chai.assert.equal(content, "Welcome thatman"); + }); + }); +}); \ No newline at end of file diff --git a/test/test_messageprocessor.ts b/test/test_messageprocessor.ts index 2dc07b88b9259bfd1cace8ebd824d42d6eff21c9..9e3cf5d838adaeb7d06e2983e263db90a1b9a223 100644 --- a/test/test_messageprocessor.ts +++ b/test/test_messageprocessor.ts @@ -181,82 +181,6 @@ describe("MessageProcessor", () => { Chai.assert.equal(content, "Hello <img alt=\"hello\" src=\"mxc://image\" style=\"height: 1em;\"/>"); }); }); - describe("FindMentionsInPlainBody", () => { - it("processes mentioned username correctly", async () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); - const guild: any = new MockGuild("123", []); - const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { - user: { - username: "TestUsername", - id: "12345", - discriminator: "54321", - }, - })]; - Chai.assert.equal( - processor.FindMentionsInPlainBody("Hello TestUsername", members), - "Hello <@!12345>", - ); - Chai.assert.equal( - processor.FindMentionsInPlainBody("Hello TestUsername#54321", members), - "Hello <@!12345>", - ); - }); - it("processes mentioned nickname correctly", async () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); - const guild: any = new MockGuild("123", []); - const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { - nick: "Test", - user: { - username: "Test", - id: "54321", - }, - }), new Discord.GuildMember(guild, { - nick: "TestNickname", - user: { - username: "TestUsername", - id: "12345", - }, - })]; - Chai.assert.equal(processor.FindMentionsInPlainBody("Hello TestNickname", members), "Hello <@!12345>"); - Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname: Hello", members), "<@!12345>: Hello"); - Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname, Hello", members), "<@!12345>, Hello"); - Chai.assert.equal(processor.FindMentionsInPlainBody("TestNickname Hello", members), "<@!12345> Hello"); - Chai.assert.equal(processor.FindMentionsInPlainBody("testNicKName Hello", members), "<@!12345> Hello"); - Chai.assert.equal( - processor.FindMentionsInPlainBody("I wish TestNickname was here", members), - "I wish <@!12345> was here", - ); - Chai.assert.equal( - processor.FindMentionsInPlainBody("I wish TestNickname was here, TestNickname is cool", members), - "I wish <@!12345> was here, <@!12345> is cool", - ); - Chai.assert.equal( - processor.FindMentionsInPlainBody("TestNickname was here with Test", members), - "<@!12345> was here with <@!54321>", - ); - }); - it("processes non-mentions correctly", async () => { - const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot); - const guild: any = new MockGuild("123", []); - const members: Discord.GuildMember[] = [new Discord.GuildMember(guild, { - nick: "that", - user: { - username: "TestUsername", - id: "12345", - }, - }), - new Discord.GuildMember(guild, { - nick: "testingstring", - user: { - username: "that", - id: "12345", - }, - })]; - const msg = "Welcome thatman"; - const content = processor.FindMentionsInPlainBody(msg, members); - Chai.assert.equal(content, "Welcome thatman"); - }); - }); describe("InsertEmbeds", () => { it("processes titleless embeds properly", () => { const processor = new MessageProcessor(new MessageProcessorOpts("localhost"), <DiscordBot> bot);