diff --git a/src/bot.ts b/src/bot.ts index ed6288719cd2a42a10826bfc28a6bade143c756a..a4bbeb404f81ff578926d8412aa6abd3bef2172e 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -12,7 +12,6 @@ import * as Discord from "discord.js"; import * as log from "npmlog"; import * as Bluebird from "bluebird"; import * as mime from "mime"; -import * as path from "path"; import { Provisioner } from "./provisioner"; // Due to messages often arriving before we get a response from the send call, @@ -181,15 +180,13 @@ export class DiscordBot { } const embed = this.mxEventProcessor.EventToEmbed(event, profile, chan); const opts: Discord.MessageOptions = {}; - const hasAttachment = ["m.image", "m.audio", "m.video", "m.file"].indexOf(event.content.msgtype) !== -1; - if (hasAttachment) { - const attachment = await Util.DownloadFile(mxClient.mxcUrlToHttp(event.content.url)); - const name = this.GetFilenameForMediaEvent(event.content); - opts.file = { - name, - attachment, - }; + const file = await this.mxEventProcessor.HandleAttachment(event, mxClient); + if (typeof(file) === "string") { + embed.description += " " + file; + } else { + opts.file = file; } + let msg = null; let hook: Discord.Webhook ; if (botUser) { @@ -207,18 +204,12 @@ export class DiscordBot { try { if (!botUser) { msg = await chan.send(embed.description, opts); - } else if (hook && !hasAttachment) { - const hookOpts: Discord.WebhookMessageOptions = { - username: embed.author.name, - avatarURL: embed.author.icon_url, - }; - // Uncomment this and remove !hasAttachment above after https://github.com/hydrabolt/discord.js/pull/1449 pulled - // if (hasAttachment) { - // hookOpts.file = opts.file; - // msg = await hook.send(embed.description, hookOpts); - // } else { - msg = await hook.send(embed.description, hookOpts); - // } + } else if (hook) { + msg = await hook.send(embed.description, { + username: embed.author.name, + avatarURL: embed.author.icon_url, + file: opts.file, + }); } else { opts.embed = embed; msg = await chan.send("", opts); @@ -329,16 +320,6 @@ export class DiscordBot { return dbEmoji.MxcUrl; } - private GetFilenameForMediaEvent(content): string { - if (content.body) { - if (path.extname(content.body) !== "") { - return content.body; - } - return path.basename(content.body) + "." + mime.extension(content.info.mimetype); - } - return "matrix-media." + mime.extension(content.info.mimetype); - } - private GetRoomIdsFromChannel(channel: Discord.Channel): Promise<string[]> { return this.bridge.getRoomStore().getEntriesByRemoteRoomData({ discord_channel: channel.id, diff --git a/src/matrixeventprocessor.ts b/src/matrixeventprocessor.ts index 3a4abb9ca3d28de944f9397aed631ec2ba15c82d..9595abbd0c198e955ba5c20923a09bf9139a0701 100644 --- a/src/matrixeventprocessor.ts +++ b/src/matrixeventprocessor.ts @@ -3,6 +3,12 @@ import {MessageProcessorOpts, MessageProcessor} from "./messageprocessor"; import {DiscordBot} from "./bot"; import {DiscordBridgeConfig} from "./config"; import * as escapeStringRegexp from "escape-string-regexp"; +import {Util} from "./util"; +import * as path from "path"; +import * as mime from "mime"; +import * as log from "npmlog"; + +const MaxFileSize = 8000000; export class MatrixEventProcessorOpts { constructor( @@ -85,4 +91,39 @@ export class MatrixEventProcessor { return body; } + public async HandleAttachment(event: any, mxClient: any): Promise<string|Discord.FileOptions> { + const hasAttachment = ["m.image", "m.audio", "m.video", "m.file"].indexOf(event.content.msgtype) !== -1; + if (!hasAttachment) { + return ""; + } + if (event.content.info == null) { + log.info("Event was an attachment type but was missing a content.info"); + return ""; + } + + let size = event.content.info.size || 0; + const url = mxClient.mxcUrlToHttp(event.content.url); + const name = this.GetFilenameForMediaEvent(event.content); + if (size < MaxFileSize) { + const attachment = await Util.DownloadFile(url); + size = attachment.byteLength; + if (size < MaxFileSize) { + return { + name, + attachment, + }; + } + } + return `[${name}](${url})`; + } + + private GetFilenameForMediaEvent(content: any): string { + if (content.body) { + if (path.extname(content.body) !== "") { + return content.body; + } + return path.basename(content.body) + "." + mime.extension(content.info.mimetype); + } + return "matrix-media." + mime.extension(content.info.mimetype); + } } diff --git a/test/test_matrixeventprocessor.ts b/test/test_matrixeventprocessor.ts index bec92ad263f26894c0b1f3971dc1e52fae1c528a..67636e33740bc5d0a98b487a80efd63b6688fc8f 100644 --- a/test/test_matrixeventprocessor.ts +++ b/test/test_matrixeventprocessor.ts @@ -4,7 +4,6 @@ import * as log from "npmlog"; import * as Discord from "discord.js"; import * as Proxyquire from "proxyquire"; -// import * as Proxyquire from "proxyquire"; import { PresenceHandler } from "../src/presencehandler"; import { DiscordBot } from "../src/bot"; import { MockGuild } from "./mocks/guild"; @@ -29,17 +28,19 @@ const bot = { }, }; +const mxClient = { + mxcUrlToHttp: (url) => { + return url.replace("mxc://", "https://"); + }, +}; + function createMatrixEventProcessor (disableMentions: boolean = false, disableEveryone = false, disableHere = false): MatrixEventProcessor { const bridge = { getClientFactory: () => { return { getClientAs: () => { - return { - mxcUrlToHttp: () => { - return "https://madeup.com"; - }, - }; + return mxClient; }, }; }, @@ -48,7 +49,16 @@ function createMatrixEventProcessor config.bridge.disableDiscordMentions = disableMentions; config.bridge.disableEveryoneMention = disableEveryone; config.bridge.disableHereMention = disableHere; - return new MatrixEventProcessor( + return new (Proxyquire("../src/matrixeventprocessor", { + "./util": { + Util: { + DownloadFile: (name: string) => { + const size = parseInt(name.substring(name.lastIndexOf("/") + 1), undefined); + return Buffer.alloc(size); + }, + }, + }, + })).MatrixEventProcessor( new MatrixEventProcessorOpts( config, bridge, @@ -71,7 +81,7 @@ describe("MatrixEventProcessor", () => { avatar_url: "mxc://localhost/avatarurl", }, 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.icon_url, "https://localhost/avatarurl"); Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); @@ -109,9 +119,9 @@ describe("MatrixEventProcessor", () => { content: { body: "testcontent", }, - }, {avatar_url: "test"}, mockChannel as any); + }, {avatar_url: "mxc://localhost/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.icon_url, "https://localhost/test"); Chai.assert.equal(evt.author.url, "https://matrix.to/#/@test:localhost"); }); @@ -235,4 +245,75 @@ describe("MatrixEventProcessor", () => { Chai.assert.equal(content, "Welcome thatman"); }); }); + describe("HandleAttachment", () => { + it("message without an attachment", () => { + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.text", + }, + }, mxClient)).to.eventually.eq(""); + }); + it("message without an info", () => { + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.video", + }, + }, mxClient)).to.eventually.eq(""); + }); + it("message without an info", () => { + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.video", + }, + }, mxClient)).to.eventually.eq(""); + }); + it("message with a large info.size", () => { + const LARGE_FILE = 8000000; + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.video", + info: { + size: LARGE_FILE, + }, + body: "filename.webm", + url: "mxc://localhost/8000000", + }, + }, mxClient)).to.eventually.eq("[filename.webm](https://localhost/8000000)"); + }); + it("message with a small info.size", () => { + const SMALL_FILE = 200; + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.video", + info: { + size: SMALL_FILE, + }, + body: "filename.webm", + url: "mxc://localhost/200", + }, + }, mxClient)).to.eventually.satisfy((attachment) => { + expect(attachment.name).to.eq("filename.webm"); + expect(attachment.attachment.length).to.eq(SMALL_FILE); + return true; + }); + }); + it("message with a small info.size but a larger file", () => { + const processor = createMatrixEventProcessor(); + return expect(processor.HandleAttachment({ + content: { + msgtype: "m.video", + info: { + size: 200, + }, + body: "filename.webm", + url: "mxc://localhost/8000000", + }, + }, mxClient)).to.eventually.eq("[filename.webm](https://localhost/8000000)"); + }); + }); });