const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants; const events = require("../lib/events.js"); const {hasFlag} = require("../lib/guildSettings.js"); const REGEX_GITHUB = /(?:\s|^)(\|\|\s*)?https?:\/\/(www\.)?github\.com\/[a-z0-9-]+\/[a-z0-9-]+\/blob\/([a-z0-9-_.#/]*)(\s*\|\|)?/gi; const REGEX_GITLAB = /(?:\s|^)(\|\|\s*)?https?:\/\/.+?\/[a-z0-9-]+\/[a-z0-9-]+\/-\/blob\/([a-z0-9-_.#/]*)(\s*\|\|)?/gi; const REGEX_GITEA = /(?:\s|^)(\|\|\s*)?https?:\/\/.+?\/[a-z0-9-]+\/[a-z0-9-]+\/src\/branch\/([a-z0-9-_.#/]*)(\s*\|\|)?/gi; const REGEX_SPOILER = /(?:\s|^)\|\|([\s\S]+?)\|\|/; 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"), ""); } async function processFile(link, spoiler = false) { link = link.replaceAll("||", "").trim(); const res = await fetch(link); if (!res.ok) return ""; if (!res.headers.get("Content-Type").startsWith("text/plain")) return ""; const file = await res.text(); const lines = file.replace(/\r/g, "").split("\n"); const fileName = link.substring( link.lastIndexOf("/") + 1, link.indexOf("#") == -1 ? link.length : link.indexOf("#") ); const fileType = fileName.lastIndexOf(".") == -1 ? "" : fileName.substring(fileName.lastIndexOf(".") + 1); if (fileType == "md") return ""; const lineStr = link.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 && lines.length > 20) return ""; let targetLines = ( entireFile ? lines : lines.slice(startLine - 1, endLine) ).join("\n"); 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"; } return `**${fileName}: **${whichLines}${warning}\n${ spoiler ? "||" : "" }\`\`\`${fileType}\n${unindent(targetLines)}\n\`\`\`${spoiler ? "||" : ""}`; } events.add("messageCreate", "codePreviews", async function (msg) { if (msg.author.id == hf.bot.user.id) return; if (!msg.guildID) return; if (!(await hasFlag(msg.guildID, "codePreviews"))) return; const files = []; const githubLinks = msg.content.match(REGEX_GITHUB); const gitlabLinks = msg.content.match(REGEX_GITLAB); const giteaLinks = msg.content.match(REGEX_GITEA); if (githubLinks?.length) { for (const link of githubLinks) { const spoiler = REGEX_SPOILER.test(link); files.push(await processFile(link.replace("/blob/", "/raw/"), spoiler)); } } if (gitlabLinks?.length) { for (const link of gitlabLinks) { const spoiler = REGEX_SPOILER.test(link); files.push(await processFile(link.replace("/blob/", "/raw/"), spoiler)); } } if (giteaLinks?.length) { for (const link of giteaLinks) { const spoiler = REGEX_SPOILER.test(link); files.push(await processFile(link.replace("/src/", "/raw/"), spoiler)); } } let out = ""; const allFiles = files.join("\n").trim(); if (allFiles !== "" && allFiles.length <= 2000) { 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, }, }); } } }); // TODO: maybe all command outputs should have this ability events.add( "messageReactionAdd", "codePreviews", async function (msg, reaction, reactor) { if (!msg.guildID) return; if (!(await hasFlag(msg.guildID, "codePreviews"))) return; if (reaction.name != "\u274c") return; let channel = msg.channel; if (!channel.name) { channel = hf.bot.getChannel(channel.id); } if (!msg.messageReference) { msg = await channel.getMessage(msg.id); } if (!msg.messageReference) return; const ref = await channel.getMessage(msg.messageReference.messageID); if (!ref) return; if ( ref.author.id != reactor.id && !channel.permissionsOf(reactor.id).has("manageMessages") ) return; if ( !REGEX_GITHUB.test(ref.content) && !REGEX_GITLAB.test(ref.content) && !REGEX_GITEA.test(ref.content) ) return; await msg.delete("Author requested code preview deletion"); } );