diff --git a/github/GithubApp.ts b/github/GithubApp.ts index 8659bde..b83a262 100644 --- a/github/GithubApp.ts +++ b/github/GithubApp.ts @@ -38,14 +38,48 @@ import { import { githubWebHooks } from "./endpoints/githubEndpoints"; import { IJobContext } from "@rocket.chat/apps-engine/definition/scheduler"; import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; -import { clearInteractionRoomData, getInteractionRoomData } from "./persistance/roomInteraction"; +import { + clearInteractionRoomData, + getInteractionRoomData, +} from "./persistance/roomInteraction"; import { GHCommand } from "./commands/GhCommand"; - -export class GithubApp extends App { +import { + IPreMessageSentExtend, + IMessage, +} from "@rocket.chat/apps-engine/definition/messages"; +import { IMessageExtender } from "@rocket.chat/apps-engine/definition/accessors"; +import { hasCodeLink, isGithubLink } from "./helpers/checkLinks"; +import { handleCodeLink } from "./handlers/handleLinks"; +export class GithubApp extends App implements IPreMessageSentExtend { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { super(info, logger, accessors); } + public async checkPreMessageSentExtend( + message: IMessage, + read: IRead, + http: IHttp + ): Promise { + if (await isGithubLink(message)) { + return true; + } + return false; + } + + public async executePreMessageSentExtend( + message: IMessage, + extend: IMessageExtender, + read: IRead, + http: IHttp, + persistence: IPersistence + ): Promise { + + if(await hasCodeLink(message)){ + await handleCodeLink(message,read,http,message.sender,message.room,extend); + } + return extend.getMessage(); + } + public async authorizationCallback( token: IAuthData, user: IUser, @@ -63,7 +97,10 @@ export class GithubApp extends App { }, }; let text = `GitHub Authentication Succesfull 🚀`; - let interactionData = await getInteractionRoomData(read.getPersistenceReader(),user.id) ; + let interactionData = await getInteractionRoomData( + read.getPersistenceReader(), + user.id + ); if (token) { // await registerAuthorizedUser(read, persistence, user); @@ -71,15 +108,14 @@ export class GithubApp extends App { } else { text = `Authentication Failure 😔`; } - if(interactionData && interactionData.roomId){ + if (interactionData && interactionData.roomId) { let roomId = interactionData.roomId as string; - let room = await read.getRoomReader().getById(roomId) as IRoom; - await clearInteractionRoomData(persistence,user.id); - await sendNotification(read,modify,user,room,text); - }else{ + let room = (await read.getRoomReader().getById(roomId)) as IRoom; + await clearInteractionRoomData(persistence, user.id); + await sendNotification(read, modify, user, room, text); + } else { await sendDirectMessage(read, modify, user, text, persistence); } - } public oauth2ClientInstance: IOAuth2Client; public oauth2Config: IOAuth2ClientOptions = { diff --git a/github/app.json b/github/app.json index 58af157..0d2452e 100644 --- a/github/app.json +++ b/github/app.json @@ -12,5 +12,7 @@ "nameSlug": "github", "classFile": "GithubApp.ts", "description": "The ultimate app extending Rocket.Chat for all developers collaborating on Github", - "implements": [] + "implements": [ + "IPreMessageSentExtend" + ] } \ No newline at end of file diff --git a/github/handlers/handleLinks.ts b/github/handlers/handleLinks.ts new file mode 100644 index 0000000..48a32d7 --- /dev/null +++ b/github/handlers/handleLinks.ts @@ -0,0 +1,57 @@ +import { IUser } from "@rocket.chat/apps-engine/definition/users"; +import { IHttp, IRead } from "@rocket.chat/apps-engine/definition/accessors"; +import { + IMessage, + IMessageAttachment, +} from "@rocket.chat/apps-engine/definition/messages"; +import { IRoom } from "@rocket.chat/apps-engine/definition/rooms"; +import { IMessageExtender } from "@rocket.chat/apps-engine/definition/accessors"; +import { TextObjectType } from "@rocket.chat/apps-engine/definition/uikit"; + +async function checkLines(content, url) { + const regex: RegExp = /(?:L(\d+)+-L(\d+)|L(\d+))/; + const match: RegExpMatchArray = url.match(regex); + if (match[2]) { + const endLine = parseInt(match[2]) - parseInt(match[1]); + const lines = new RegExp( + `(?:.*\n){${parseInt(match[1]) - 1}}(.*(?:\n.*){${endLine}})` + ); + const text = await content.match(lines); + return text[1]; + } else if (match[3]) { + const lines = new RegExp( + `(?:.*\n){${parseInt(match[3]) - 1}}(.*(?:\n.*){5})` + ); + const text = await content.match(lines); + return text[1]; + } else { + const lines = new RegExp(`(?:.*\n){0}(.*(?:\n.*){5})`); + const text = await content.match(lines); + return text[1]; + } +} + +export async function handleCodeLink( + message: IMessage, + read: IRead, + http: IHttp, + user: IUser, + room: IRoom, + extend: IMessageExtender +) { + const regex: RegExp = /\bhttps?:\/\/github\.com\/\S+\b/; + let text = message.text!; + const match: RegExpMatchArray | null = text.match(regex); + const result: string | undefined = match?.[0]; + let url: string = result?.replace("blob/", "")!; + url = url.replace("github.com", "raw.githubusercontent.com"); + let response: any = await http.get(url); + const { content } = response; + let code = await checkLines(content, url); + + let attachment: IMessageAttachment = { + text: `\`\`\`\n${code}\n\`\`\` \n[Show more...](${result})`, + type: TextObjectType.MARKDOWN, + }; + extend.addAttachment(attachment); +} diff --git a/github/helpers/checkLinks.ts b/github/helpers/checkLinks.ts new file mode 100644 index 0000000..fb7440b --- /dev/null +++ b/github/helpers/checkLinks.ts @@ -0,0 +1,19 @@ +import { IMessage } from "@rocket.chat/apps-engine/definition/messages"; + +export async function hasCodeLink(message: IMessage): Promise { + let lineNo: RegExp = + /https?:\/\/github\.com\/[A-Za-z0-9_-]+\/[A-Za-z0-9_.-]+\/blob\/[A-Za-z0-9_-]+\/.+/; + + if (lineNo.test(message.text!)) { + return true; + } + return false; +} + +export async function isGithubLink(message: IMessage) { + let githubLink: RegExp = /(?:https?:\/\/)?(?:www\.)?github\.com\//; + if (githubLink.test(message.text!)) { + return true; + } + return false; +} diff --git a/github/package-lock.json b/github/package-lock.json index 2a28c63..77f1fd6 100644 --- a/github/package-lock.json +++ b/github/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "@rocket.chat/apps-engine": "^1.19.0", + "@rocket.chat/apps-engine": "^1.36.0", "@types/node": "14.14.6", "tslint": "^5.10.0", "typescript": "^4.0.5" @@ -47,17 +47,18 @@ } }, "node_modules/@rocket.chat/apps-engine": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.32.0.tgz", - "integrity": "sha512-K+5NJto1BfO0NFiJX048tAWJqNQjdHGg5YPB24MQ/cDq206hDPnJakihF02LgardAQShIK3TIw2MnKgX1CB2/w==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.36.0.tgz", + "integrity": "sha512-PnG7fuO4Rq8FYf9JVU5mybhACRP8Ftj+XSHBKZ1vOuoEa1U32Rdmb9F012iawnI7wgP/K0vYp/bY3HH2N2Bv0w==", "dev": true, "dependencies": { - "adm-zip": "^0.4.9", + "adm-zip": "^0.5.9", "cryptiles": "^4.1.3", "lodash.clonedeep": "^4.5.0", - "semver": "^5.5.0", + "semver": "^5.7.1", "stack-trace": "0.0.10", - "uuid": "^3.2.1" + "uuid": "^3.4.0", + "vm2": "^3.9.11" } }, "node_modules/@types/node": { @@ -66,13 +67,34 @@ "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", "dev": true }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", "dev": true, "engines": { - "node": ">=0.3.0" + "node": ">=6.0" } }, "node_modules/ansi-styles": { @@ -516,6 +538,22 @@ "uuid": "bin/uuid" } }, + "node_modules/vm2": { + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + }, + "bin": { + "vm2": "bin/vm2" + }, + "engines": { + "node": ">=6.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -551,17 +589,18 @@ } }, "@rocket.chat/apps-engine": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.32.0.tgz", - "integrity": "sha512-K+5NJto1BfO0NFiJX048tAWJqNQjdHGg5YPB24MQ/cDq206hDPnJakihF02LgardAQShIK3TIw2MnKgX1CB2/w==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.36.0.tgz", + "integrity": "sha512-PnG7fuO4Rq8FYf9JVU5mybhACRP8Ftj+XSHBKZ1vOuoEa1U32Rdmb9F012iawnI7wgP/K0vYp/bY3HH2N2Bv0w==", "dev": true, "requires": { - "adm-zip": "^0.4.9", + "adm-zip": "^0.5.9", "cryptiles": "^4.1.3", "lodash.clonedeep": "^4.5.0", - "semver": "^5.5.0", + "semver": "^5.7.1", "stack-trace": "0.0.10", - "uuid": "^3.2.1" + "uuid": "^3.4.0", + "vm2": "^3.9.11" } }, "@types/node": { @@ -570,10 +609,22 @@ "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", "dev": true }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", "dev": true }, "ansi-styles": { @@ -921,6 +972,16 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "vm2": { + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",