HiddenPhox/src/modules/codePreviews.js

257 lines
6.9 KiB
JavaScript
Raw Permalink Normal View History

2024-05-18 23:50:53 +00:00
const {ApplicationCommandOptionTypes, MessageFlags} =
require("@projectdysnomia/dysnomia").Constants;
const InteractionCommand = require("../lib/interactionCommand.js");
const {getOption} = require("../lib/interactionDispatcher.js");
2022-12-06 17:44:54 +00:00
2022-05-07 22:49:07 +00:00
const events = require("../lib/events.js");
const {hasFlag} = require("../lib/guildSettings.js");
const REGEX_GITHUB =
2024-06-02 04:58:46 +00:00
/(?:\s|^)(\|\|\s*)?https?:\/\/(www\.)?github\.com\/[a-z0-9-]+\/[a-z0-9-._]+\/blob\/([a-z0-9-_.?&=#/%]*)(\s*\|\|)?/gi;
2022-05-07 22:49:07 +00:00
const REGEX_GITLAB =
2024-06-02 04:58:46 +00:00
/(?:\s|^)(\|\|\s*)?https?:\/\/.+?\/[a-z0-9-]+\/[a-z0-9-._]+\/-\/blob\/([a-z0-9-_.?&=#/%]*)(\s*\|\|)?/gi;
2022-05-07 22:49:07 +00:00
const REGEX_GITEA =
2024-06-02 04:58:46 +00:00
/(?:\s|^)(\|\|\s*)?https?:\/\/.+?\/[a-z0-9-]+\/[a-z0-9-._]+\/src\/(branch|commit)\/([a-z0-9-_.?&=#/%]*)(\s*\|\|)?/gi;
2023-12-05 18:17:35 +00:00
const REGEX_SPOILER = /(?:\s|^)\|\|([\s\S]+?)\|\|/;
2022-05-07 22:49:07 +00:00
function unindent(str) {
str = str.replace(/\t/g, " ");
const minIndent =
str
.match(/^ *(?=\S)/gm)
?.reduce((prev, curr) => Math.min(prev, curr.length), Infinity) ?? 0;
if (!minIndent) return str;
return str.replace(new RegExp(`^ {${minIndent}}`, "gm"), "");
}
2024-06-02 05:17:43 +00:00
const fileTypeAliases = {
astro: "jsx",
svelte: "jsx",
vue: "jsx",
mdx: "md",
jsonc: "json",
json5: "json",
jsonld: "json",
"sublime-build": "json",
"sublime-settings": "json",
"sublime-menu": "json",
"sublime-commands": "json",
"sublime-project": "json",
"sublime-mousemap": "json",
"sublime-keymap": "json",
"sublime-macro": "json",
"sublime-completions": "json",
"code-workspace": "json",
"code-snippets": "json",
};
2024-05-18 23:50:53 +00:00
async function processFile(
link,
originalLink,
spoiler = false,
linkFile = false
) {
link = link.replaceAll("||", "").trim();
const res = await fetch(link);
if (!res.ok) return "";
2023-06-15 03:42:40 +00:00
if (!res.headers.get("Content-Type").startsWith("text/plain")) return "";
const file = await res.text();
const lines = file.replace(/\r/g, "").split("\n");
const urlObj = new URL(link);
let fileName = decodeURIComponent(urlObj.pathname).substring(
2024-06-02 04:35:42 +00:00
urlObj.pathname.lastIndexOf("/") + 1,
urlObj.pathname.length
);
const fileType =
fileName.lastIndexOf(".") == -1
? ""
: fileName.substring(fileName.lastIndexOf(".") + 1);
2024-05-18 23:50:53 +00:00
if (linkFile) {
fileName = `[${fileName}](<${originalLink}>)`;
}
const lineStr = urlObj.hash.match(/#L\d+(-L?\d+)?/)?.[0];
let startLine, endLine;
let entireFile = false;
if (lineStr) {
const [start, end] = lineStr.match(/\d+/g);
if (!end) {
startLine = endLine = start;
} else {
startLine = start;
endLine = end;
}
} else {
entireFile = true;
startLine = 0;
endLine = lines.length;
}
const whichLines = entireFile
? ""
: startLine == endLine
? "Line " + startLine
: "Lines " + startLine + "-" + endLine;
if (entireFile) {
if (fileType == "md") return "";
if (lines.length > 20) return "";
}
2023-12-05 18:17:35 +00:00
let targetLines = (
entireFile ? lines : lines.slice(startLine - 1, endLine)
).join("\n");
2023-12-05 18:17:35 +00:00
let warning = "";
if (spoiler && targetLines.includes("||")) {
targetLines = targetLines.replaceAll("||", "|\u200b|");
warning = " - :warning: Zero width spaces present";
}
if (targetLines.includes("``")) {
targetLines = targetLines.replaceAll("``", "`\u200b`");
warning = " - :warning: Zero width spaces present";
}
2024-05-18 23:50:53 +00:00
return `**${fileName}:** ${whichLines}${warning}\n${
2023-12-05 18:17:35 +00:00
spoiler ? "||" : ""
2024-06-02 05:17:43 +00:00
}\`\`\`${fileTypeAliases[fileType] ?? fileType}\n${unindent(
targetLines
)}\n\`\`\`${spoiler ? "||" : ""}`;
}
2022-05-07 22:49:07 +00:00
events.add("messageCreate", "codePreviews", async function (msg) {
if (msg.author.id == hf.bot.user.id) return;
2022-05-07 22:49:07 +00:00
if (!msg.guildID) return;
2022-05-07 23:59:02 +00:00
if (!(await hasFlag(msg.guildID, "codePreviews"))) return;
2022-05-07 22:49:07 +00:00
const files = [];
2022-05-07 22:49:07 +00:00
const githubLinks = msg.content.match(REGEX_GITHUB);
const gitlabLinks = msg.content.match(REGEX_GITLAB);
const giteaLinks = msg.content.match(REGEX_GITEA);
2022-05-07 22:51:27 +00:00
if (githubLinks?.length) {
for (const link of githubLinks) {
2023-12-05 18:17:35 +00:00
const spoiler = REGEX_SPOILER.test(link);
2024-05-18 23:50:53 +00:00
files.push(
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
);
}
}
if (gitlabLinks?.length) {
for (const link of gitlabLinks) {
2023-12-05 18:17:35 +00:00
const spoiler = REGEX_SPOILER.test(link);
2024-05-18 23:50:53 +00:00
files.push(
await processFile(link.replace("/blob/", "/raw/"), link, spoiler)
);
}
}
if (giteaLinks?.length) {
for (const link of giteaLinks) {
2023-12-05 18:17:35 +00:00
const spoiler = REGEX_SPOILER.test(link);
2024-05-18 23:50:53 +00:00
files.push(
await processFile(link.replace("/src/", "/raw/"), link, spoiler)
);
2022-05-07 22:51:27 +00:00
}
2022-05-07 22:49:07 +00:00
}
let out = "";
const allFiles = files.join("\n").trim();
if (allFiles !== "" && allFiles.length <= 2000) {
2023-01-22 04:45:57 +00:00
await msg.edit({flags: MessageFlags.SUPPRESS_EMBEDS}).catch(() => {});
}
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (file === "") continue;
if (out.length + file.length > 2000) {
await msg.channel.createMessage({
content: out,
allowedMentions: {
repliedUser: false,
},
messageReference: {
messageID: msg.id,
},
});
out = file;
} else {
out += "\n" + file;
out = out.trim();
}
if (i == files.length - 1 && out.length <= 2000) {
await msg.channel.createMessage({
content: out,
allowedMentions: {
repliedUser: false,
},
messageReference: {
messageID: msg.id,
},
});
}
2022-05-07 23:10:31 +00:00
}
2022-05-07 22:49:07 +00:00
});
2024-05-18 23:50:53 +00:00
const codepreviewsCommand = new InteractionCommand("codepreview");
codepreviewsCommand.helpText =
"Post snippets of codes from files on GitHub, Gitlab and Gitea instances.";
codepreviewsCommand.options.url = {
name: "url",
type: ApplicationCommandOptionTypes.STRING,
description: "URL to attempt to parse",
required: true,
default: "",
};
codepreviewsCommand.options.spoiler = {
name: "spoiler",
type: ApplicationCommandOptionTypes.BOOLEAN,
description: "Send spoilered",
required: false,
default: false,
};
codepreviewsCommand.callback = async function (interaction) {
const url = getOption(interaction, codepreviewsCommand, "url");
const spoiler = getOption(interaction, codepreviewsCommand, "spoiler");
const githubOrGitlab = url.match(REGEX_GITHUB) ?? url.match(REGEX_GITLAB);
const gitea = url.match(REGEX_GITEA);
let out = "";
if (githubOrGitlab) {
out = await processFile(url.replace("/blob/", "/raw/"), url, spoiler, true);
} else if (gitea) {
out = await processFile(url.replace("/src/", "/raw/"), url, spoiler, true);
} else {
return {
content: "Provided link did not match any services.",
flags: MessageFlags.EPHEMERAL,
};
}
if (out == "") {
return {
content:
"No content was returned. Provided file is either too long, a markdown file, or not plaintext.",
flags: MessageFlags.EPHEMERAL,
};
}
if (out.length > 2000) {
return {
content: "Provided file is too long.",
flags: MessageFlags.EPHEMERAL,
};
}
return out;
};
hf.registerCommand(codepreviewsCommand);