Skip to content
Merged
31 changes: 30 additions & 1 deletion github/GithubApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
IConfigurationExtend,
IHttp,
ILogger,
IMessageExtender,
IModify,
IPersistence,
IRead,
Expand Down Expand Up @@ -45,12 +46,40 @@ 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 { GHCommand } from "./commands/GhCommand";
import { IPreMessageSentExtend, IMessage } from "@rocket.chat/apps-engine/definition/messages";
import { handleGitHubCodeSegmentLink } from "./handlers/GitHubCodeSegmentHandler";
import { isGithubLink, hasGitHubCodeSegmentLink } from "./helpers/checkLinks";

export class GithubApp extends App {
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<boolean> {
if (await isGithubLink(message)) {
return true;
}
return false;
}

public async executePreMessageSentExtend(
message: IMessage,
extend: IMessageExtender,
read: IRead,
http: IHttp,
persistence: IPersistence
): Promise<IMessage> {

if (await hasGitHubCodeSegmentLink(message)) {
await handleGitHubCodeSegmentLink(message, read, http, message.sender, message.room, extend);
}
return extend.getMessage();
}

public async authorizationCallback(
token: IAuthData,
user: IUser,
Expand Down
4 changes: 3 additions & 1 deletion github/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
5 changes: 5 additions & 0 deletions github/enum/GitHubURL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GitHubURLEnum {
PREFIX = "blob/",
HOST = "github.com",
RAW_HOST = "raw.githubusercontent.com",
}
69 changes: 69 additions & 0 deletions github/handlers/GitHubCodeSegmentHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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";
import { GitHubURLEnum } from "../enum/GitHubURL";

async function extractCodeSnippetFromURL(content: string, url: string): Promise<string> {
const lineRangeRegex: RegExp = /(?:L(\d+)+-L(\d+)|L(\d+))/;
const lineRangeMatch: RegExpMatchArray | null = url.match(lineRangeRegex);

if (lineRangeMatch) {
return extractCodeSnippetByLineRange(content, lineRangeMatch);
}

return "";
}

function extractCodeSnippetByLineRange(content: string, lineRangeMatch: RegExpMatchArray): string {
const [_, startLine, endLine, singleLine] = lineRangeMatch;

const lineOffset = singleLine ? parseInt(singleLine) : parseInt(startLine) - 1;
const lineCount = singleLine ? 1 : parseInt(endLine) - parseInt(startLine) + 1;

const linesRegex = `(?:.*\n){${lineOffset}}(.*(?:\n.*){${lineCount}})`;
const lines = new RegExp(linesRegex);
const match = content.match(lines);

return match?.[1] ?? "";
}

async function fetchGitHubContent(http: IHttp, modifiedUrl: string): Promise<string> {
const response: any = await http.get(modifiedUrl);
const { content } = response;
return content;
}

function buildCodeSnippetAttachment(codeSnippet: string, url: string): IMessageAttachment {
const attachment: IMessageAttachment = {
text: `\`\`\`\n${codeSnippet}\n\`\`\` \n[Show more...](${url})`,
type: TextObjectType.MARKDOWN,
};
return attachment;
}

export async function handleGitHubCodeSegmentLink(
message: IMessage,
read: IRead,
http: IHttp,
user: IUser,
room: IRoom,
extend: IMessageExtender
) {
const urlRegex: RegExp = /\bhttps?:\/\/github\.com\/\S+\b/;
const messageText: string = message.text!;
const urlMatch: RegExpMatchArray | null = messageText.match(urlRegex);
const url: string | undefined = urlMatch?.[0];
let modifiedUrl: string = url?.replace(GitHubURLEnum.PREFIX, "")!;
modifiedUrl = modifiedUrl.replace(GitHubURLEnum.HOST, GitHubURLEnum.RAW_HOST);

const content: string = await fetchGitHubContent(http, modifiedUrl);
const codeSnippet = await extractCodeSnippetFromURL(content, modifiedUrl);

if (codeSnippet) {
const attachment: IMessageAttachment = buildCodeSnippetAttachment(codeSnippet, url!);
extend.addAttachment(attachment);
}
}
19 changes: 19 additions & 0 deletions github/helpers/checkLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IMessage } from "@rocket.chat/apps-engine/definition/messages";

export async function hasGitHubCodeSegmentLink(message: IMessage): Promise<Boolean> {
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;
}