diff --git a/src/modules/codePreviews.js b/src/modules/codePreviews.js index 276fa70..df7584d 100644 --- a/src/modules/codePreviews.js +++ b/src/modules/codePreviews.js @@ -1,4 +1,7 @@ -const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants; +const {ApplicationCommandOptionTypes, MessageFlags} = + require("@projectdysnomia/dysnomia").Constants; +const InteractionCommand = require("../lib/interactionCommand.js"); +const {getOption} = require("../lib/interactionDispatcher.js"); const events = require("../lib/events.js"); const {hasFlag} = require("../lib/guildSettings.js"); @@ -20,7 +23,12 @@ function unindent(str) { return str.replace(new RegExp(`^ {${minIndent}}`, "gm"), ""); } -async function processFile(link, spoiler = false) { +async function processFile( + link, + originalLink, + spoiler = false, + linkFile = false +) { link = link.replaceAll("||", "").trim(); const res = await fetch(link); if (!res.ok) return ""; @@ -28,7 +36,7 @@ async function processFile(link, spoiler = false) { const file = await res.text(); const lines = file.replace(/\r/g, "").split("\n"); - const fileName = decodeURI(link).substring( + let fileName = decodeURIComponent(link).substring( link.lastIndexOf("/") + 1, link.indexOf("#") == -1 ? link.length : link.indexOf("#") ); @@ -37,6 +45,10 @@ async function processFile(link, spoiler = false) { ? "" : fileName.substring(fileName.lastIndexOf(".") + 1); + if (linkFile) { + fileName = `[${fileName}](<${originalLink}>)`; + } + if (fileType == "md") return ""; const lineStr = link.match(/#L\d+(-L?\d+)?/)?.[0]; @@ -79,7 +91,7 @@ async function processFile(link, spoiler = false) { warning = " - :warning: Zero width spaces present"; } - return `**${fileName}: **${whichLines}${warning}\n${ + return `**${fileName}:** ${whichLines}${warning}\n${ spoiler ? "||" : "" }\`\`\`${fileType}\n${unindent(targetLines)}\n\`\`\`${spoiler ? "||" : ""}`; } @@ -98,21 +110,27 @@ events.add("messageCreate", "codePreviews", async function (msg) { if (githubLinks?.length) { for (const link of githubLinks) { const spoiler = REGEX_SPOILER.test(link); - files.push(await processFile(link.replace("/blob/", "/raw/"), spoiler)); + files.push( + await processFile(link.replace("/blob/", "/raw/"), link, spoiler) + ); } } if (gitlabLinks?.length) { for (const link of gitlabLinks) { const spoiler = REGEX_SPOILER.test(link); - files.push(await processFile(link.replace("/blob/", "/raw/"), spoiler)); + files.push( + await processFile(link.replace("/blob/", "/raw/"), link, spoiler) + ); } } if (giteaLinks?.length) { for (const link of giteaLinks) { const spoiler = REGEX_SPOILER.test(link); - files.push(await processFile(link.replace("/src/", "/raw/"), spoiler)); + files.push( + await processFile(link.replace("/src/", "/raw/"), link, spoiler) + ); } } @@ -155,3 +173,58 @@ events.add("messageCreate", "codePreviews", async function (msg) { } } }); + +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); diff --git a/src/modules/utility.js b/src/modules/utility.js index 4aabfc9..804162d 100644 --- a/src/modules/utility.js +++ b/src/modules/utility.js @@ -40,6 +40,8 @@ const APP_ICON_BASE = CDN + "app-icons/"; const APP_ASSET_BASE = CDN + "app-assets/"; const DISCOVERY_SPLASH_BASE = CDN + "discovery-splashes/"; const AVATAR_DECORATION_BASE = CDN + "avatar-decoration-presets/"; +const CLAN_BADGE_BASE = CDN + "clan-badges/"; +const CLAN_BANNER_BASE = CDN + "clan-banner/"; const DEFAULT_GROUP_DM_AVATARS = [ "/assets/ee9275c5a437f7dc7f9430ba95f12ebd.png", @@ -349,6 +351,15 @@ const CHANNEL_TYPE_NAMES = { 16: "media", }; +const CLAN_PLAYSTYLE = { + 0: "None", + 1: "Very Casual", + 2: "Casual", + 3: "Competitive", + 4: "Creative", + 5: "Very Competitive", +}; + const EMOJI_SETS = { blobs: { prefix: @@ -452,7 +463,17 @@ async function getGuild(id) { ); if (widget) return {source: "widget", data: widget}; } catch { - return null; + try { + const verification = await hf.bot.requestHandler.request( + "GET", + `/guilds/${id}/member-verification?with_guild=true`, + true + ); + if (verification?.guild) + return {source: "verification", data: verification.guild}; + } catch { + return null; + } } } } @@ -1870,7 +1891,7 @@ guildinfo.addAlias("server"); guildinfo.addAlias("sinfo"); guildinfo.addAlias("si"); guildinfo.callback = async function (msg, line) { - let _guild; + let _guild, clanEmbed; if (!line || line == "") { if (!msg.guildID) return "Not in a guild."; _guild = {source: "local", data: msg.channel.guild}; @@ -1878,6 +1899,88 @@ guildinfo.callback = async function (msg, line) { if (!SNOWFLAKE_REGEX.test(line)) return "Not a snowflake."; const snowflake = line.match(SNOWFLAKE_REGEX)[1]; _guild = await getGuild(snowflake); + + try { + const clan = await hf.bot.requestHandler.request( + "GET", + `/discovery/${snowflake}/clan`, + true + ); + + if (clan) { + const images = []; + + clanEmbed = { + name: _guild != null ? "Clan data" : clan.name, + description: clan.description ?? "*No description*", + fields: [ + !_guild && { + name: "Member Count", + value: clan.member_count, + inline: true, + }, + { + name: "Tag", + value: clan.tag, + inline: true, + }, + { + name: "Playstyle", + value: + CLAN_PLAYSTYLE[clan.playstyle] ?? + ``, + }, + { + name: "Descriptors", + value: clan.wildcard_descriptors.join(", "), + inline: true, + }, + { + name: "Interests/Topics/Traits", + value: `\`${clan.search_terms.map((x) => `"${x}"`).join(", ")}\``, + inline: false, + }, + { + name: "Associated Game IDs", + value: `\`${clan.game_ids.map((x) => `"${x}"`).join(", ")}\``, + inline: false, + }, + { + name: "Badge Colors", + value: `${clan.badge_color_primary} | ${clan.badge_color_secondary}`, + inline: true, + }, + { + name: "Banner/Brand Colors", + value: `${clan.brand_color_primary} | ${clan.brand_color_secondary}`, + inline: true, + }, + ].filter((x) => !!x), + footer: !_guild ? {text: "Fetched from clan"} : null, + }; + + if (clan.badge_hash) { + const url = `${CLAN_BADGE_BASE}${clan.id}/${clan.badge_hash}.png?size=4096`; + images.push(`[Badge](${url})`); + clanEmbed.thumbnail = {url}; + } + if (clan.banner_hash) { + const url = `${CLAN_BANNER_BASE}${clan.id}/${clan.banner_hash}.png?size=4096`; + images.push(`[Banner](${url})`); + clanEmbed.image = {url}; + } + + if (images.length > 0) { + clanEmbed.fields.push({ + name: "\u200b", + value: images.join(" | "), + inline: false, + }); + } + } + } catch { + // noop + } } if (!_guild) return "Guild not found."; @@ -2124,9 +2227,14 @@ guildinfo.callback = async function (msg, line) { }); } - return {embed}; + if (clanEmbed) { + return {embeds: [embed, clanEmbed]}; + } else { + return {embed}; + } } - case "preview": { + case "preview": + case "verification": { const embed = { title: guild.name, description: guild.description ?? "*No description.*", @@ -2136,7 +2244,12 @@ guildinfo.callback = async function (msg, line) { value: ``, inline: true, }, - guild.emojis.length > 0 && { + guild.verification_level && { + name: "Verification Level", + value: GUILD_VERIFICATION_LEVELS[guild.verification_level], + inline: true, + }, + (guild.emojis?.length ?? 0) > 0 && { name: `Emotes (${guild.emojis.length})`, value: `${ guild.emojis.filter((e) => e.animated).length @@ -2147,7 +2260,7 @@ guildinfo.callback = async function (msg, line) { } unavailable`, inline: true, }, - guild.stickers.length > 0 && { + (guild.stickers?.length ?? 0) > 0 && { name: `Stickers (${guild.stickers.length})`, value: `${ guild.stickers.filter((s) => s.format_type == 1).length @@ -2164,7 +2277,11 @@ guildinfo.callback = async function (msg, line) { }, ].filter((x) => !!x), footer: { - text: "Fetched from guild preview", + text: `Fetched from ${ + _guild.source === "verification" + ? "membership screening" + : "guild preview" + }`, }, }; @@ -2235,7 +2352,11 @@ guildinfo.callback = async function (msg, line) { }); } - return {embed}; + if (clanEmbed) { + return {embeds: [embed, clanEmbed]}; + } else { + return {embed}; + } } case "discovery": { if (!guild.store_page) { @@ -2359,7 +2480,11 @@ guildinfo.callback = async function (msg, line) { }); } - return {embed}; + if (clanEmbed) { + return {embeds: [embed, clanEmbed]}; + } else { + return {embed}; + } } case "widget": { let invite; @@ -2391,7 +2516,7 @@ guildinfo.callback = async function (msg, line) { if (invite) { embed.fields.push({ name: "Invite", - value: "https://discord.gg/" + guild.instant_invite, + value: guild.instant_invite, inline: true, }); @@ -2432,7 +2557,7 @@ guildinfo.callback = async function (msg, line) { const images = []; if (invite.guild.icon) { images.push( - `[Icon](${ICON_BASE}${invite.guild.id}/${invite.guild.icon}.png?size=4096` + `[Icon](${ICON_BASE}${invite.guild.id}/${invite.guild.icon}.png?size=4096)` ); } if (invite.guild.splash) { @@ -2461,7 +2586,11 @@ guildinfo.callback = async function (msg, line) { }); } - return {embed}; + if (clanEmbed) { + return {embeds: [embed, clanEmbed]}; + } else { + return {embed}; + } } default: return "Guild not found.";