diff --git a/src/commands/github.ts b/src/commands/github.ts new file mode 100644 index 0000000..0f16a8b --- /dev/null +++ b/src/commands/github.ts @@ -0,0 +1,6 @@ +import { defineCommand } from "../loaders/commands.ts"; + +defineCommand({ + name: "github", + description: "Search on github" +}) diff --git a/src/listeners/message_create.ts b/src/listeners/message_create.ts index 5a8abd7..c91ff86 100644 --- a/src/listeners/message_create.ts +++ b/src/listeners/message_create.ts @@ -1,14 +1,19 @@ -import { Events, Message } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Events, Message } from "discord.js"; import { defineListener } from "../loaders/listeners.ts"; import { MESSAGE_PREFIX } from "../constants.ts"; import { COMMANDS } from "../loaders/commands.ts"; import { MessageCommandContext } from "../structs/context/CommandContext.ts"; +import { extname } from "node:path"; +import { safeSlice } from "../util.ts"; + +const GITHUB_LINE_URL_REGEX = /^(?:https?:\/\/)?(?:www\.)?(?:github)\.com\/(?[a-zA-Z0-9-_]+\/[A-Za-z0-9_.-]+)\/blob\/(?.+?)#L(?\d+)[-~]?L?(?\d*)/i; defineListener({ event: Events.MessageCreate, run: async(message: Message) => { - if (!message.content.toLowerCase().startsWith(MESSAGE_PREFIX)) - return; + if (message.system || message.author.bot) return; + + if (!message.content.toLowerCase().startsWith(MESSAGE_PREFIX)) return handleOthers(message); const [commandName, ...args] = message.content .slice(MESSAGE_PREFIX.length) @@ -30,3 +35,56 @@ defineListener({ }); } }); + +function handleOthers(message: Message) { + handleGithubLink(message); +} + +async function handleGithubLink(message: Message) { + const match = GITHUB_LINE_URL_REGEX.exec(message.content); + const groups = match?.groups; + if (!groups) return; + + const repo = groups.repo; + const path = groups.path; + const extension = extname(path).slice(1); + const firstLineNumber = parseInt(groups.first_line_number) - 1; + const secondLineNumber = parseInt(groups.second_line_number) || firstLineNumber + 1; + + // limit, max 25 lines - possible flood + if (secondLineNumber - firstLineNumber > 25) { + message.react("❌"); + return; + } + + const contentUrl = `https://raw.githubusercontent.com/${repo}/${path}`; + const response = await fetch(contentUrl); + const content = await response.text(); + const lines = content.split("\n"); + + let text = ""; + + for (let i = 0; i < lines.length; i++) { + if (i < firstLineNumber || i >= secondLineNumber) continue; + + const line = lines[i]; + text += `${line}\n`; + } + + // delete the last \n + text = text.slice(0, -1); + + message.reply({ + content: `\`\`\`${extension}\n${safeSlice(text, 2000 - 6 - extension.length)}\n\`\`\``, + components: [ + new ActionRowBuilder() + .setComponents( + new ButtonBuilder() + .setLabel(repo) + .setStyle(ButtonStyle.Link) + .setURL(`https://github.com/${repo}`) + ) + .toJSON() + ] + }) +} diff --git a/src/util.ts b/src/util.ts index 8a6b01d..5c6964d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ -export function safeSlice(array: T[], length: number) { - return array.length > length ? array.slice(0, length) : array; +export function safeSlice(input: T[] | string, length: number) { + return input.length > length ? input.slice(0, length) : input; } export async function silently(value: Promise) {