diff --git a/package.json b/package.json index 5a399cc0..f1ae2db5 100644 --- a/package.json +++ b/package.json @@ -13,20 +13,27 @@ }, "dependencies": { "@octokit/rest": "^16.28.7", - "@types/express": "^4.17.0", - "@types/ioredis": "^4.0.13", - "@types/markdown-it": "^0.0.8", "express": "^4.17.1", "ioredis": "^4.14.0", "markdown-it": "^9.0.1", "matrix-bot-sdk": "^0.3.9", "micromatch": "^4.0.2", + "mime": "^2.4.4", + "mocha": "^6.2.0", + "node-emoji": "^1.10.0", + "request-promise-native": "^1.0.7", "winston": "^3.2.1", "yaml": "^1.6.0" }, "devDependencies": { + "@types/express": "^4.17.0", + "@types/ioredis": "^4.0.13", + "@types/markdown-it": "^0.0.8", "@types/micromatch": "^3.1.0", + "@types/mime": "^2.0.1", "@types/node": "^12.6.9", + "@types/node-emoji": "^1.8.1", + "@types/request-promise-native": "^1.0.16", "@types/yaml": "^1.0.2", "typescript": "^3.5.3" } diff --git a/src/CommentProcessor.ts b/src/CommentProcessor.ts index c78d5027..990150f7 100644 --- a/src/CommentProcessor.ts +++ b/src/CommentProcessor.ts @@ -1,9 +1,13 @@ import { IssuesGetCommentResponse } from "@octokit/rest"; import { Appservice } from "matrix-bot-sdk"; +import request from "request-promise-native"; import markdown from "markdown-it"; +import mime from "mime"; +import emoji from "node-emoji"; const md = new markdown(); const REGEX_MENTION = /(^|\s)(@[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38})(\s|$)/ig; +const REGEX_IMAGES = /!\[.*]\((.*\.(\w+))\)/gm; interface IMatrixCommentEvent { msgtype: string; @@ -19,15 +23,16 @@ interface IMatrixCommentEvent { export class CommentProcessor { constructor (private as: Appservice) {} - public getEventBodyForComment(comment: IssuesGetCommentResponse): IMatrixCommentEvent { + public async getEventBodyForComment(comment: IssuesGetCommentResponse): Promise { let body = comment.body; - body = body.replace(REGEX_MENTION, (_match: string, _part1: string, githubId: string) => { - const userId = this.as.getUserIdForSuffix(githubId.substr(1)); - return `[$2](https://matrix.to/#/${userId})`; - }); + body = this.replaceMentions(body); + body = await this.replaceImages(body); + body = emoji.emojify(body); + let htmlBody = md.render(body); + console.log(body, htmlBody); return { body, - formatted_body: md.render(body), + formatted_body: htmlBody, msgtype: "m.text", format: "org.matrix.custom.html", external_url: comment.html_url, @@ -36,4 +41,37 @@ export class CommentProcessor { }, } } + + private replaceMentions(body: string): string { + return body.replace(REGEX_MENTION, (_match: string, _part1: string, githubId: string) => { + const userId = this.as.getUserIdForSuffix(githubId.substr(1)); + return `[$2](https://matrix.to/#/${userId})`; + }); + } + + private async replaceImages(body: string): Promise { + let match; + let bodyCopy = body; + let urlMatches: string[] = []; + while(match = REGEX_IMAGES.exec(bodyCopy)) { + bodyCopy = bodyCopy.replace(match[1], ""); + const contentType = mime.getType(match[1]) || "none"; + if (!contentType.startsWith("image") && !contentType.startsWith("video") && !contentType.startsWith("audio")) { + // Not handling media. + continue; + } + urlMatches.push(match[1]); + } + for (const rawUrl of urlMatches) { + try { + const imageData = await request.get(rawUrl, { encoding: null}); + const contentType = mime.getType(rawUrl) || "application/octet-stream"; + const url = await this.as.botIntent.underlyingClient.uploadContent(imageData, contentType); + body = body.replace(rawUrl, url); + } catch (ex) { + console.warn("Failed to upload file"); + } + } + return body; + } } \ No newline at end of file