Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Expand code Link #116

Merged
merged 10 commits into from
Jul 9, 2023
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;
}