diff --git a/.prettierrc b/.prettierrc index 15520fe..7a6cfbd 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { - "semi": true, - "bracketSpacing": false, - "endOfLine": "lf" + "semi": true, + "bracketSpacing": false, + "endOfLine": "lf", + "printWidth": 120 } diff --git a/src/index.js b/src/index.js index 597fd32..14e0421 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,4 @@ -const Dysnomia = require("@projectdysnomia/dysnomia"); -const logger = require("./lib/logger.js"); +const {Client, Collection, Channel, Permission} = require("@projectdysnomia/dysnomia"); const fs = require("node:fs"); const {resolve} = require("node:path"); const sqlite3 = require("sqlite3"); @@ -8,22 +7,25 @@ const {instead, before} = require("spitroast"); const config = require("../config.json"); const apikeys = require("../apikeys.json"); +const logger = require("./lib/logger.js"); const events = require("./lib/events.js"); const timer = require("./lib/timer.js"); const Command = require("./lib/command.js"); const InteractionCommand = require("./lib/interactionCommand.js"); -const bot = new Dysnomia.Client(config.token, { +const {APIEndpoints, Intents, ApplicationCommandTypes, GatewayOPCodes} = require("./util/dconstants.js"); + +const bot = new Client(config.token, { defaultImageFormat: "png", defaultImageSize: 1024, gateway: { - intents: Object.values(Dysnomia.Constants.Intents), + intents: Object.values(Intents), }, restMode: true, }); -const commands = new Dysnomia.Collection(); -const interactionCommands = new Dysnomia.Collection(); +const commands = new Collection(); +const interactionCommands = new Collection(); const database = new sqlite3.Database(resolve(__dirname, "..", "database.db")); @@ -33,9 +35,7 @@ function registerCommand(cmdObj) { const aliases = cmdObj.getAliases(); logger.info( "hf:cmd", - `Registered command '${cmdObj.name}'${ - aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : "" - }` + `Registered command '${cmdObj.name}'${aliases.length > 0 ? ` (aliases: ${aliases.join(", ")})` : ""}` ); } else if (cmdObj instanceof InteractionCommand) { interactionCommands.set(cmdObj.name, cmdObj); @@ -55,57 +55,51 @@ global.hf = { database, }; -const {formatUsername} = require("./lib/utils.js"); +const {formatUsername} = require("./util/misc.js"); const CommandDispatcher = require("./lib/commandDispatcher.js"); const {InteractionDispatcher} = require("./lib/interactionDispatcher.js"); +const {hasFlag} = require("./lib/guildData.js"); for (const file of fs.readdirSync(resolve(__dirname, "modules"))) { - require(resolve(__dirname, "modules", file)); - logger.info("hf:modules", `Loaded module: '${file}'`); + try { + require(resolve(__dirname, "modules", file)); + logger.info("hf:modules", `Loaded module: "${file}"`); + } catch (err) { + logger.error("hf:modules", `Failed to load "${file}": ${err}`); + } } bot.on("messageCreate", async (msg) => { try { // fix DMs cause of gateway v8 changes - if ( - !(msg.channel instanceof Dysnomia.Channel) && - msg.author.id != bot.user.id && - !msg.guildID - ) { + if (!(msg.channel instanceof Channel) && msg.author.id != bot.user.id && !msg.guildID) { const newChannel = await bot.getDMChannel(msg.author.id); if (msg.channel.id == newChannel.id) msg.channel = newChannel; } - if (!(msg.channel instanceof Dysnomia.Channel)) return; + if (!(msg.channel instanceof Channel)) return; + if (msg.author.bot && msg.guildID) { + const canBot = await hasFlag(msg.guildID, "replyToBots"); + if (!canBot) return; + } await CommandDispatcher(msg); } catch (err) { const stack = (err?.stack ?? err.message).split("\n"); const error = stack.shift(); - logger.error( - "hf:main", - `Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}` - ); + logger.error("hf:main", `Failed to dispatch command: ${error}\n\t${stack.join("\n\t")}`); } }); bot.on("messageUpdate", async (msg, oldMsg) => { try { const oneDay = Date.now() - 86400000; - if ( - msg.timestamp > oneDay && - !msg.hasRan && - oldMsg && - oldMsg.content !== msg.content - ) { + if (msg.timestamp > oneDay && !msg.hasRan && oldMsg && oldMsg.content !== msg.content) { await CommandDispatcher(msg); } } catch (err) { const stack = (err?.stack ?? err.message).split("\n"); const error = stack.shift(); - logger.error( - "hf:main", - `Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}` - ); + logger.error("hf:main", `Failed to dispatch command update: ${error}\n\t${stack.join("\n\t")}`); } }); bot.on("messageReactionAdd", async (msg, reaction, reactor) => { @@ -114,7 +108,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => { try { let channel = msg.channel; - if (!(channel instanceof Dysnomia.Channel)) { + if (!(channel instanceof Channel)) { const newChannel = hf.bot.getChannel(channel.id); if (newChannel) { channel = newChannel; @@ -137,10 +131,7 @@ bot.on("messageReactionAdd", async (msg, reaction, reactor) => { } catch (err) { const stack = (err?.stack ?? err.message).split("\n"); const error = stack.shift(); - logger.error( - "hf:main", - `Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}` - ); + logger.error("hf:main", `Failed to self-delete message: ${error}\n\t${stack.join("\n\t")}`); } }); bot.on("interactionCreate", async (interaction) => { @@ -149,21 +140,13 @@ bot.on("interactionCreate", async (interaction) => { } catch (err) { const stack = (err?.stack ?? err.message).split("\n"); const error = stack.shift(); - logger.error( - "hf:main", - `Failed to dispatch interaction command: ${error}\n\t${stack.join( - "\n\t" - )}` - ); + logger.error("hf:main", `Failed to dispatch interaction command: ${error}\n\t${stack.join("\n\t")}`); } }); bot.once("ready", async () => { logger.info("hf:main", "Connected to Discord."); - logger.info( - "hf:main", - `Logged in as: ${formatUsername(bot.user)} (${bot.user.id})` - ); + logger.info("hf:main", `Logged in as: ${formatUsername(bot.user)} (${bot.user.id})`); const channel = await bot.getDMChannel(config.owner_id); if (channel) { @@ -202,25 +185,18 @@ bot.once("ready", async () => { formattedCommand.contexts.push(1, 2); } - if (command.type === Dysnomia.Constants.ApplicationCommandTypes.CHAT_INPUT) + if (command.type === ApplicationCommandTypes.CHAT_INPUT) formattedCommand.name = formattedCommand.name.toLowerCase(); if (command.permissions !== undefined) { formattedCommand.default_member_permissions = - command.permissions instanceof Dysnomia.Permission - ? String(command.permissions.allow) - : String(command.permissions); + command.permissions instanceof Permission ? String(command.permissions.allow) : String(command.permissions); } commands.push(formattedCommand); } - await bot.requestHandler.request( - "PUT", - `/applications/${bot.application.id}/commands`, - true, - commands - ); + await bot.requestHandler.request("PUT", APIEndpoints.COMMANDS(bot.application.id), true, commands); }); bot.on("error", (err) => { @@ -243,10 +219,7 @@ bot.on("shardReady", (id) => { logger.verbose("hf:shard", `Shard ${id} ready`); }); bot.on("unknown", (packet, id) => { - logger.verbose( - "hf:main", - `Shard ${id} caught unknown packet: ${JSON.stringify(packet)}` - ); + logger.verbose("hf:main", `Shard ${id} caught unknown packet: ${JSON.stringify(packet)}`); }); instead("spawn", bot.shards, function (args, orig) { @@ -254,7 +227,7 @@ instead("spawn", bot.shards, function (args, orig) { const shard = this.get(args[0]); if (shard) { before("sendWS", shard.__proto__, function ([op, _data]) { - if (op === Dysnomia.Constants.GatewayOPCodes.IDENTIFY) { + if (op === GatewayOPCodes.IDENTIFY) { _data.properties.browser = "Discord Embedded"; delete _data.properties.device; } diff --git a/src/lib/commandDispatcher.js b/src/lib/commandDispatcher.js index c0a6965..cf47be3 100644 --- a/src/lib/commandDispatcher.js +++ b/src/lib/commandDispatcher.js @@ -1,5 +1,5 @@ const logger = require("./logger.js"); -const {pastelize, getTopColor} = require("./utils.js"); +const {pastelize, getTopColor} = require("../util/misc.js"); function convertIfApplicable(val) { if (isNaN(val)) { @@ -27,10 +27,7 @@ function parseAsArgv(args) { for (let i = 0; i < args.length; i++) { const arg = args.shift(); const equalsIndex = arg.charAt(0) === "-" ? arg.indexOf("=") : -1; - const argName = - equalsIndex === -1 - ? removeStartHyphens(arg) - : removeStartHyphens(arg.slice(0, equalsIndex)); + const argName = equalsIndex === -1 ? removeStartHyphens(arg) : removeStartHyphens(arg.slice(0, equalsIndex)); if (equalsIndex !== -1) { optional[argName] = convertIfApplicable(arg.slice(equalsIndex + 1)); @@ -176,9 +173,7 @@ async function CommandDispatcher(msg) { } if (response.embeds) { for (const embed of response.embeds) { - embed.color = - embed.color || - getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id)); + embed.color = embed.color || getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id)); } } if (response.reaction) { @@ -186,17 +181,14 @@ async function CommandDispatcher(msg) { } else { try { const outMessage = await msg.channel.createMessage( - Object.assign( - typeof response === "string" ? {content: response} : response, - { - allowedMentions: { - repliedUser: false, - }, - messageReference: { - messageID: msg.id, - }, - } - ) + Object.assign(typeof response === "string" ? {content: response} : response, { + allowedMentions: { + repliedUser: false, + }, + messageReference: { + messageID: msg.id, + }, + }) ); if (response.addReactions) { for (const index in response.addReactions) { diff --git a/src/lib/events.js b/src/lib/events.js index fbaeee5..ee80181 100644 --- a/src/lib/events.js +++ b/src/lib/events.js @@ -17,10 +17,7 @@ function add(event, identifier, callback) { try { callback.apply(this, arguments); } catch (error) { - logger.error( - "hf:event", - `Event "${event}:${identifier}" failed: ${error}` - ); + logger.error("hf:event", `Event "${event}:${identifier}" failed: ${error}`); } } diff --git a/src/lib/guildData.js b/src/lib/guildData.js index aae13ec..c51e035 100644 --- a/src/lib/guildData.js +++ b/src/lib/guildData.js @@ -1,6 +1,4 @@ -hf.database.run( - "CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID" -); +hf.database.run("CREATE TABLE IF NOT EXISTS guild_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"); function setGuildData(id, key, value) { return new Promise((resolve, reject) => { diff --git a/src/lib/guildSettings.js b/src/lib/guildSettings.js index f1b0071..0b69408 100644 --- a/src/lib/guildSettings.js +++ b/src/lib/guildSettings.js @@ -2,7 +2,7 @@ const {getGuildData, setGuildData} = require("./guildData.js"); const flags = Object.freeze({ codePreviews: 1 << 0, - tweetUnrolling: 1 << 1, + replyToBots: 1 << 1, fedimbed: 1 << 2, }); @@ -18,9 +18,7 @@ async function getFlags(guildId) { } async function hasFlag(guildId, key) { - return ( - ((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0 - ); + return ((await getGuildData(guildId, "settings_flags", 0)) & flags[key]) !== 0; } async function enableFlag(guildId, key) { diff --git a/src/lib/interactionCommand.js b/src/lib/interactionCommand.js index ba7fa4e..f07181a 100644 --- a/src/lib/interactionCommand.js +++ b/src/lib/interactionCommand.js @@ -1,5 +1,4 @@ -const {ApplicationCommandTypes, ApplicationCommandOptionTypes} = - require("@projectdysnomia/dysnomia").Constants; +const {ApplicationCommandTypes, ApplicationCommandOptionTypes} = require("../util/dconstants.js"); class InteractionCommand { constructor(name) { @@ -18,6 +17,10 @@ class InteractionCommand { }; } + getOption(interaction, key) { + return interaction.data.options?.find((o) => o.name === key)?.value ?? this.options[key].default; + } + callback() { return "Callback not overwritten."; } diff --git a/src/lib/interactionDispatcher.js b/src/lib/interactionDispatcher.js index 6ecbae5..02403c0 100644 --- a/src/lib/interactionDispatcher.js +++ b/src/lib/interactionDispatcher.js @@ -1,13 +1,6 @@ const logger = require("./logger.js"); -const {MessageFlags} = require("@projectdysnomia/dysnomia").Constants; -const {pastelize, getTopColor} = require("./utils.js"); - -function getOption(interaction, command, key) { - return ( - interaction.data.options?.find((o) => o.name === key)?.value ?? - command.options[key].default - ); -} +const {MessageFlags} = require("../util/dconstants.js"); +const {pastelize, getTopColor} = require("../util/misc.js"); async function runCommand(interaction, command) { if (command.ownerOnly && interaction.user.id != hf.config.owner_id) { @@ -17,10 +10,7 @@ async function runCommand(interaction, command) { }; } - if ( - command.elevatedOnly && - !hf.config.elevated.includes(interaction.user.id) - ) { + if (command.elevatedOnly && !hf.config.elevated.includes(interaction.user.id)) { return { content: "No\n\nSent from my iPhone", flags: MessageFlags.EPHEMERAL, @@ -45,7 +35,7 @@ async function runCommand(interaction, command) { async function InteractionDispatcher(interaction) { const command = hf.interactionCommands.get(interaction.data.name); - const shouldSend = getOption(interaction, command, "send"); + const shouldSend = command.getOption(interaction, "send"); const ack = interaction.acknowledge(shouldSend ? 0 : MessageFlags.EPHEMERAL); try { @@ -86,24 +76,19 @@ async function InteractionDispatcher(interaction) { } if (response.embeds) { for (const embed of response.embeds) { - embed.color = - embed.color ?? - getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id)); + embed.color = embed.color ?? getTopColor(interaction, hf.bot.user.id, pastelize(hf.bot.user.id)); } } try { await ack; await interaction.createMessage( - Object.assign( - typeof response === "string" ? {content: response} : response, - { - flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL, - allowedMentions: { - repliedUser: false, - }, - } - ) + Object.assign(typeof response === "string" ? {content: response} : response, { + flags: shouldSend ? response.flags : MessageFlags.EPHEMERAL, + allowedMentions: { + repliedUser: false, + }, + }) ); } catch (err) { await ack; @@ -128,4 +113,4 @@ async function InteractionDispatcher(interaction) { } } -module.exports = {InteractionDispatcher, getOption}; +module.exports = {InteractionDispatcher}; diff --git a/src/lib/logger.js b/src/lib/logger.js index e1a3247..2756368 100644 --- a/src/lib/logger.js +++ b/src/lib/logger.js @@ -5,16 +5,7 @@ const BRIGHT = "[9"; const NORMAL_BG = "[4"; const BRIGHT_BG = "[10"; -const NAMES = [ - "Black", - "Red", - "Green", - "Yellow", - "Blue", - "Magenta", - "Cyan", - "White", -]; +const NAMES = ["Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"]; const COLORS = {}; for (const index in NAMES) { @@ -73,6 +64,5 @@ function baseLogger(prefix, level, ...message) { module.exports = {}; for (const level in LEVEL_COLORS) { - module.exports[level] = (prefix, ...message) => - baseLogger.apply(null, [prefix, level, ...message]); + module.exports[level] = (prefix, ...message) => baseLogger.apply(null, [prefix, level, ...message]); } diff --git a/src/lib/userData.js b/src/lib/userData.js index 7910960..fa1128c 100644 --- a/src/lib/userData.js +++ b/src/lib/userData.js @@ -1,6 +1,4 @@ -hf.database.run( - "CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID" -); +hf.database.run("CREATE TABLE IF NOT EXISTS user_data (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID"); function setUserData(id, key, value) { return new Promise((resolve, reject) => { diff --git a/src/lib/utils.js b/src/lib/utils.js deleted file mode 100644 index 5aa6d55..0000000 --- a/src/lib/utils.js +++ /dev/null @@ -1,440 +0,0 @@ -const {Collection} = require("@projectdysnomia/dysnomia"); -const murmurhash = require("murmurhash").v3; -const {tinycolor} = require("@ctrl/tinycolor"); - -const logger = require("../lib/logger.js"); - -function pastelize(id) { - const hue = murmurhash(id) % 360; - const hex = tinycolor(`hsl(${hue},75%,60%)`).toHex(); - return parseInt(hex, 16); -} - -function formatUsername(user) { - return user.discriminator && user.discriminator != "0" - ? `${user.username}#${user.discriminator}` - : `@${user.username}`; -} - -function getTopColor(msg, id, fallback = 0x7289da) { - if (!msg.guildID) return fallback; - const guild = msg.channel?.guild ?? hf.bot.guilds.get(msg.guildID); - if (!guild) return fallback; - - const roles = guild.members - .get(id) - .roles.map((role) => guild.roles.get(role)) - .filter((role) => role.color); - roles.sort((a, b) => b.position - a.position); - - return roles[0]?.color || fallback; -} - -function safeString(string, newLines = true) { - string = string ? string.toString() : ""; - string = string.replace(/`/g, "'"); - string = string.replace(/<@/g, "<@\u200b"); - string = string.replace(/<#/g, "<#\u200b"); - string = string.replace(/<&/g, "<&\u200b"); - if (newLines) string = string.replace(/\n/g, " "); - return string; -} - -function formatTime(number) { - let seconds = parseInt(number) / 1000; - const days = Math.floor(seconds / 86400); - seconds = seconds % 86400; - const hours = Math.floor(seconds / 3600); - seconds = seconds % 3600; - const minutes = Math.floor(seconds / 60); - seconds = Math.floor(seconds % 60); - - return ( - (days !== 0 ? `${days.toString().padStart(2, "0")}:` : "") + - (hours !== 0 ? `${hours.toString().padStart(2, "0")}:` : "") + - `${minutes.toString().padStart(2, "0")}:${seconds - .toString() - .padStart(2, "0")}` - ); -} - -function readableTime(number) { - const seconds = number / 1000; - const days = seconds / 60 / 60 / 24; - const years = days / 365.25; - - if (years >= 1) { - return `${years.toFixed(2)} years`; - } else { - return `${days.toFixed(2)} days`; - } -} - -async function isGif(url) { - const type = await fetch(url, {method: "HEAD"}).then((res) => - res.headers.get("Content-Type") - ); - return type == "image/gif"; -} - -async function findLastImage(msg, gif = false) { - const messages = await msg.channel.getMessages(20); - let img; - - for (const message of messages) { - if (message.attachments.size > 0) { - img = [...msg.attachments.values()][0].url; - if (gif && (await isGif(img))) { - break; - } else { - break; - } - } else if (message.embeds.length > 0) { - img = message.embeds[0]?.thumbnail?.url || message.embeds[0]?.image?.url; - if (img) { - if (gif && (await isGif(img))) { - break; - } else { - break; - } - } - } - } - - return await new Promise((resolve, reject) => { - if (!img) { - reject("Image not found in last 20 messages."); - } else { - resolve(img); - } - }); -} - -const urlRegex = /((https?):\/)?\/?([^:/\s]+)((\/\w+)*\/)([\w\-.]+)/; - -async function getImage(msg, str) { - const refMsg = msg.referencedMessage; - if (refMsg) { - const attachments = [...refMsg.attachments.values()]; - if (attachments[0]?.url) { - return attachments[0].url; - } else if (//.test(refMsg.content)) { - const id = refMsg.content.match(//)[1]; - return `https://cdn.discordapp.com/emojis/${id}.png?v=1`; - } else if (/<@!?([0-9]+)>/.test(refMsg.content)) { - const id = refMsg.content.match(/<@!?([0-9]+)>/)[1]; - const user = await hf.bot.requestHandler.request( - "GET", - "/users/" + id, - true - ); - if (user) - return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`; - } - } - - const img = await findLastImage(msg, false); - if (!str) { - if (img) return img; - } - - const attachments = [...msg.attachments.values()]; - if (attachments[0]?.url) { - return attachments[0]?.url; - } else if (urlRegex.test(str)) { - return str; - } else if (//.test(str)) { - const id = str.match(//)[1]; - return `https://cdn.discordapp.com/emojis/${id}.png?v=1`; - } else if (/<@!?([0-9]+)>/.test(str)) { - const id = str.match(/<@!?([0-9]+)>/)[1]; - const user = await hf.bot.requestHandler.request( - "GET", - "/users/" + id, - true - ); - if (user) - return `https://cdn.discordapp.com/avatars/${id}/${user.avatar}.png?size=1024`; - } else if (img) { - return img; - } - - return null; -} - -async function hastebin(body) { - const res = await fetch(`${hf.config.haste_provider}/documents`, { - method: "POST", - body, - }).then((r) => r.json()); - return `<${hf.config.haste_provider}/${res.key}>`; -} - -hf.selectionMessages = hf.selectionMessages ?? new Collection(); -async function selectionMessage( - msg, - heading, - options, - timeout = 30000, - maxItems = 1 -) { - const data = { - content: heading, - allowedMentions: { - repliedUser: false, - }, - messageReference: { - messageID: msg.id, - }, - components: [ - { - type: 1, - components: [ - { - type: 3, - custom_id: msg.id, - options: [], - max_values: maxItems, - }, - ], - }, - { - type: 1, - components: [ - { - type: 2, - style: 4, - label: "Cancel", - custom_id: "cancel", - }, - ], - }, - ], - }; - options.slice(0, 25).forEach((value) => { - data.components[0].components[0].options.push({ - label: value.display, - value: value.key, - description: value.description, - }); - }); - if (options.length > 25) { - data.content += `\n\nDisplaying 25/${options.length} results`; - } - - const displayMessage = await msg.channel - .createMessage(data) - .catch((err) => - logger.error( - "selectionMessage", - "Failed to create selection message: " + err - ) - ); - - return await new Promise((resolve, reject) => { - function listener(interaction) { - const user = interaction.member.user || interaction.user; - - if ( - user.id == msg.author.id && - interaction.channel.id == msg.channel.id && - interaction.message.components[0].components[0].custom_id == msg.id - ) { - if (interaction.data.custom_id == "cancel") { - hf.events.remove("interactionCreate", `selection.${msg.id}`); - clearTimeout(hf.selectionMessages.get(msg.id)); - hf.selectionMessages.delete(msg.id); - - interaction.deferUpdate(); - - displayMessage.delete(); - - reject("Canceled"); - } else { - hf.events.remove("interactionCreate", `selection.${msg.id}`); - clearTimeout(hf.selectionMessages.get(msg.id)); - hf.selectionMessages.delete(msg.id); - - interaction.deferUpdate(); - - displayMessage.delete(); - - let result; - - if (maxItems > 1) { - result = options - .filter((opt) => interaction.data.values.includes(opt.key)) - .map((opt) => opt.key); - } else { - result = options.filter( - (opt) => opt.key == interaction.data.values[0] - )[0].value; - } - - resolve(result); - } - } - } - hf.events.add("interactionCreate", `selection.${msg.id}`, listener); - hf.selectionMessages.set( - msg.id, - setTimeout(() => { - hf.events.remove("interactionCreate", `selection.${msg.id}`); - hf.selectionMessages.delete(msg.id); - - reject("Request timed out"); - }, timeout) - ); - }); -} - -async function lookupUser(msg, str, filter) { - if (/[0-9]{17,21}/.test(str)) { - return await hf.bot.requestHandler.request( - "GET", - "/users/" + str.match(/([0-9]{17,21})/)[1], - true - ); - } - - let users; - if (filter) { - users = hf.bot.users.filter(filter).values(); - } else if (msg.guildID) { - const guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID); - users = guild.members.values(); - } else { - users = hf.bot.users.values(); - } - - if (/(.+?)#([0-9]{4})/.test(str)) { - const [_, name, discrim] = str.match(/(.+?)#([0-9]{4})/); - for (const user of users) { - if ( - user.username.toLowerCase() == name.toLowerCase() && - user.discriminator == discrim - ) { - return user; - } - } - } - - const selection = []; - for (const user of users) { - if ( - user.username.toLowerCase() == str.toLowerCase() || - (user.nickname && user.nickname == str.toLowerCase()) - ) { - return user; - } else if ( - user.username.toLowerCase().indexOf(str.toLowerCase()) > -1 || - (user.nick && user.nick.toLowerCase().indexOf(str.toLowerCase()) > -1) || - (user.globalName && - user.globalName.toLowerCase().indexOf(str.toLowerCase()) > -1) - ) { - selection.push({ - value: user, - key: user.id, - display: `${formatUsername(user)}${ - user.nick - ? ` (${user.nick})` - : user.globalName - ? ` (${user.globalName})` - : "" - }`, - }); - } - } - - selection.sort((a, b) => a.display - b.display); - - if (selection.length == 0) { - return "No results"; - } else if (selection.length == 1) { - return selection[0].value; - } else { - selection.splice(20); - - try { - return await selectionMessage( - msg, - "Multiple users found, please pick from this list:", - selection - ); - } catch (out) { - return out; - } - } -} - -// https://stackoverflow.com/a/39243641 -const htmlEntities = { - nbsp: " ", - cent: "¢", - pound: "£", - yen: "¥", - euro: "€", - copy: "©", - reg: "®", - lt: "<", - gt: ">", - quot: '"', - amp: "&", - apos: "'", -}; - -function parseHtmlEntities(str) { - return str.replace(/&([^;]+);/g, function (entity, entityCode) { - let match; - - if (entityCode in htmlEntities) { - return htmlEntities[entityCode]; - } else if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) { - return String.fromCharCode(parseInt(match[1], 16)); - } else if ((match = entityCode.match(/^#(\d+)$/))) { - return String.fromCharCode(~~match[1]); - } else { - return entity; - } - }); -} - -const UPLOAD_LIMIT = 26214400; -const UPLOAD_LIMIT_TIER_2 = 52428800; -const UPLOAD_LIMIT_TIER_3 = 104857600; - -function getUploadLimit(guild) { - if (!guild) return UPLOAD_LIMIT; - - if (guild.premiumTier == 2) return UPLOAD_LIMIT_TIER_2; - if (guild.premiumTier == 3) return UPLOAD_LIMIT_TIER_3; - - return UPLOAD_LIMIT; -} - -const TWITTER_EPOCH = 1288834974657; -const DISCORD_EPOCH = 1420070400000; -function snowflakeToTimestamp(snowflake, twitter = false) { - return ( - Math.floor(Number(snowflake) / Math.pow(2, 22)) + - (twitter == true ? TWITTER_EPOCH : DISCORD_EPOCH) - ); -} - -module.exports = { - pastelize, - formatUsername, - getTopColor, - safeString, - formatTime, - readableTime, - isGif, - findLastImage, - getImage, - hastebin, - selectionMessage, - lookupUser, - parseHtmlEntities, - getUploadLimit, - snowflakeToTimestamp, -}; diff --git a/src/modules/anonradio.js b/src/modules/anonradio.js index 364c14b..3e2eb97 100644 --- a/src/modules/anonradio.js +++ b/src/modules/anonradio.js @@ -9,28 +9,20 @@ if (hf.__anonradio_timeout) { async function updateNowPlaying() { let playing; try { - playing = await fetch("https://anonradio.net/playing").then((res) => - res.text() - ); + playing = await fetch("https://anonradio.net/playing").then((res) => res.text()); } catch (err) { try { - playing = await fetch("http://anonradio.net/playing").then((res) => - res.text() - ); + playing = await fetch("http://anonradio.net/playing").then((res) => res.text()); } catch (err) { // } } let schedule; try { - schedule = await fetch("https://anonradio.net/schedule/").then((res) => - res.text() - ); + schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text()); } catch (err) { try { - schedule = await fetch("http://anonradio.net/schedule/").then((res) => - res.text() - ); + schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text()); } catch (err) { // } @@ -68,19 +60,14 @@ async function updateNowPlaying() { } else if (playing.startsWith("Coming up")) { title = playing; } else { - const metadataLine = playing.match( - /\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/ - ); + const metadataLine = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/); const current = metadataLine?.[1] ?? "??"; const peakDay = metadataLine?.[2] ?? "??"; const peakMonth = metadataLine?.[3] ?? "??"; const dj = metadataLine?.[4] ?? "unknown"; const metadata = metadataLine?.[5] ?? "unknown"; - if ( - metadata == "https://archives.anonradio.net" || - liveNow.name == "Synth Battle Royale" - ) { + if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") { title = `${liveNow.name} (\`${dj}\`)`; } else { title = `${metadata} (\`${dj}\`)`; @@ -93,24 +80,16 @@ async function updateNowPlaying() { try { const icecast = await fetch("http://anonradio.net:8010/status-json.xsl") .then((res) => res.text()) - .then((data) => - JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')) - ); - const streamData = icecast.icestats.source.find( - (src) => src.listenurl == "http://anonradio.net:8010/openmic" - ); + .then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))); + const streamData = icecast.icestats.source.find((src) => src.listenurl == "http://anonradio.net:8010/openmic"); if (streamData && streamData.stream_start_iso8601) { const startTime = new Date(streamData.stream_start_iso8601).getTime(); title = `${streamData.title} (\`${ - streamData.server_name - ? streamData.server_name + " | " + liveNow.id - : liveNow.id + streamData.server_name ? streamData.server_name + " | " + liveNow.id : liveNow.id }\`)`; - openmicTime = `-\\*- OpenMIC DJ has been streaming since -\\*-\n`; + openmicTime = `-\\*- OpenMIC DJ has been streaming since -\\*-\n`; } - } catch (err) { + } catch { // } } @@ -129,7 +108,7 @@ async function updateNowPlaying() { content: timestamp + "\n" + content, }); } - } catch (err) { + } catch { // } } diff --git a/src/modules/bot.js b/src/modules/bot.js index 9510e63..ce7a1c1 100644 --- a/src/modules/bot.js +++ b/src/modules/bot.js @@ -9,11 +9,10 @@ const {resolve} = require("path"); const guildSettings = require("../lib/guildSettings.js"); function spawn(args) { - const shell = - process.env.SHELL || (process.platform == "win32" ? "powershell" : "sh"); + const shell = process.env.SHELL || (process.platform == "win32" ? "pwsh" : "sh"); const newArgs = []; - if (shell.match(/powershell/i) && process.platform == "win32") { + if (shell.match(/pwsh/i) && process.platform == "win32") { newArgs.push("-NoLogo", "-Command"); } else { newArgs.push("-c"); @@ -52,16 +51,18 @@ reload.callback = function (msg, line) { if (err.code == "MODULE_NOT_FOUND") { return "Module not found."; } else { + logger.info("hf:modules", `Failed to resolve "${line}": ${err}`); return `:warning: An error occurred: \`\`\`\n${err}\`\`\``; } } try { - logger.info("hf:modules", `Reloading module: '${line}'`); + logger.info("hf:modules", `Reloading module: "${line}"`); delete require.cache[require.resolve(`./${line}.js`)]; require(`./${line}.js`); return {reaction: "\uD83D\uDC4C"}; } catch (err) { + logger.info("hf:modules", `Failed to reload "${line}": ${err}`); return `:warning: An error occurred: \`\`\`\n${err}\`\`\``; } }; @@ -96,10 +97,7 @@ _eval.callback = async function (msg, line) { out = errored ? out : inspect(out, {depth: 0}); const token = hf.config.token; - out = out.replace( - new RegExp(token.replace(/\./g, "\\."), "g"), - "lol no key 4 u" - ); + out = out.replace(new RegExp(token.replace(/\./g, "\\."), "g"), "lol no key 4 u"); if (errored) { return ":warning: Output (errored):\n```js\n" + out + "\n```"; @@ -136,9 +134,7 @@ exec.callback = async function (msg, line) { }); proc.on("close", async (code) => { - out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${ - code != 0 ? "\x1b[31m" : "" - }Exited with ${code}\x1b[0m`; + out += `\n\x1b[0m\x1b[1m====\x1b[0m\n\x1b[1m${code != 0 ? "\x1b[31m" : ""}Exited with ${code}\x1b[0m`; if (out.length > 1980) { msg.channel.createMessage({ content: `Output too long to send in a message:`, @@ -188,10 +184,7 @@ settings.callback = async function (msg, line, [cmd, key, value]) { .join("\n")}\n\`\`\``; } case "enable": { - if ( - !msg.channel.permissionsOf(msg.author.id).has("manageGuild") && - !hf.config.elevated.includes(msg.author.id) - ) + if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id)) return "You do not have `Manage Server` permissions"; if (msg.author.bot) return "Zero-width space your say command."; @@ -204,10 +197,7 @@ settings.callback = async function (msg, line, [cmd, key, value]) { return {reaction: "\uD83D\uDC4C"}; } case "disable": { - if ( - !msg.channel.permissionsOf(msg.author.id).has("manageGuild") && - !hf.config.elevated.includes(msg.author.id) - ) + if (!msg.channel.permissionsOf(msg.author.id).has("manageGuild") && !hf.config.elevated.includes(msg.author.id)) return "You do not have `Manage Server` permissions"; if (msg.author.bot) return "Zero-width space your say command."; diff --git a/src/modules/codePreviews.js b/src/modules/codePreviews.js index c16202e..b682b5f 100644 --- a/src/modules/codePreviews.js +++ b/src/modules/codePreviews.js @@ -1,7 +1,5 @@ -const {ApplicationCommandOptionTypes, MessageFlags} = - require("@projectdysnomia/dysnomia").Constants; +const {ApplicationCommandOptionTypes, MessageFlags} = require("../util/dconstants.js"); const InteractionCommand = require("../lib/interactionCommand.js"); -const {getOption} = require("../lib/interactionDispatcher.js"); const events = require("../lib/events.js"); const {hasFlag} = require("../lib/guildSettings.js"); @@ -15,10 +13,7 @@ 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; + 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"), ""); } @@ -44,12 +39,7 @@ const fileTypeAliases = { "code-snippets": "json", }; -async function processFile( - link, - originalLink, - spoiler = false, - linkFile = false -) { +async function processFile(link, originalLink, spoiler = false, linkFile = false) { link = link.replaceAll("||", "").trim(); const res = await fetch(link); if (!res.ok) return ""; @@ -63,10 +53,7 @@ async function processFile( urlObj.pathname.lastIndexOf("/") + 1, urlObj.pathname.length ); - const fileType = - fileName.lastIndexOf(".") == -1 - ? "" - : fileName.substring(fileName.lastIndexOf(".") + 1); + const fileType = fileName.lastIndexOf(".") == -1 ? "" : fileName.substring(fileName.lastIndexOf(".") + 1); if (linkFile) { fileName = `[${fileName}](<${originalLink}>)`; @@ -101,9 +88,7 @@ async function processFile( if (lines.length > 20) return ""; } - let targetLines = ( - entireFile ? lines : lines.slice(startLine - 1, endLine) - ).join("\n"); + let targetLines = (entireFile ? lines : lines.slice(startLine - 1, endLine)).join("\n"); let warning = ""; if (spoiler && targetLines.includes("||")) { @@ -115,11 +100,9 @@ async function processFile( warning = " - :warning: Zero width spaces present"; } - return `**${fileName}:** ${whichLines}${warning}\n${ - spoiler ? "||" : "" - }\`\`\`${fileTypeAliases[fileType] ?? fileType}\n${unindent( - targetLines - )}\n\`\`\`${spoiler ? "||" : ""}`; + return `**${fileName}:** ${whichLines}${warning}\n${spoiler ? "||" : ""}\`\`\`${ + fileTypeAliases[fileType] ?? fileType + }\n${unindent(targetLines)}\n\`\`\`${spoiler ? "||" : ""}`; } events.add("messageCreate", "codePreviews", async function (msg) { @@ -136,27 +119,21 @@ 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/"), link, 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/"), link, 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/"), link, spoiler) - ); + files.push(await processFile(link.replace("/src/", "/raw/"), link, spoiler)); } } @@ -201,8 +178,7 @@ 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.helpText = "Post snippets of codes from files on GitHub, Gitlab and Gitea instances."; codepreviewsCommand.options.url = { name: "url", type: ApplicationCommandOptionTypes.STRING, @@ -218,8 +194,8 @@ codepreviewsCommand.options.spoiler = { default: false, }; codepreviewsCommand.callback = async function (interaction) { - const url = getOption(interaction, codepreviewsCommand, "url"); - const spoiler = getOption(interaction, codepreviewsCommand, "spoiler"); + const url = this.getOption(interaction, "url"); + const spoiler = this.getOption(interaction, "spoiler"); const githubOrGitlab = url.match(REGEX_GITHUB) ?? url.match(REGEX_GITLAB); const gitea = url.match(REGEX_GITEA); @@ -238,8 +214,7 @@ codepreviewsCommand.callback = async function (interaction) { if (out == "") { return { - content: - "No content was returned. Provided file is either too long, a markdown file, or not plaintext.", + content: "No content was returned. Provided file is either too long, a markdown file, or not plaintext.", flags: MessageFlags.EPHEMERAL, }; } diff --git a/src/modules/fedimbed.js b/src/modules/fedimbed.js index 38548ce..7c96d56 100644 --- a/src/modules/fedimbed.js +++ b/src/modules/fedimbed.js @@ -1,6 +1,5 @@ -const Dysnomia = require("@projectdysnomia/dysnomia"); -const {MessageFlags, ApplicationCommandOptionTypes, Permissions} = - Dysnomia.Constants; +const {Message} = require("@projectdysnomia/dysnomia"); +const {MessageFlags, ApplicationCommandOptionTypes, Permissions} = require("../util/dconstants.js"); const fs = require("node:fs"); const path = require("node:path"); const httpSignature = require("@peertube/http-signature"); @@ -8,22 +7,20 @@ const httpSignature = require("@peertube/http-signature"); const events = require("../lib/events.js"); const logger = require("../lib/logger.js"); const {hasFlag} = require("../lib/guildSettings.js"); -const {parseHtmlEntities, getUploadLimit} = require("../lib/utils.js"); const InteractionCommand = require("../lib/interactionCommand.js"); -const {getOption} = require("../lib/interactionDispatcher.js"); -const FRIENDLY_USERAGENT = - "HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)"; +const {getUploadLimit} = require("../util/misc.js"); +const {htmlToMarkdown} = require("../util/html.js"); -const URLS_REGEX = - /(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g; +const FRIENDLY_USERAGENT = "HiddenPhox/fedimbed (https://gitdab.com/Cynosphere/HiddenPhox)"; + +const URLS_REGEX = /(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g; const SPOILER_REGEX = /(?:\s|^)\|\|([\s\S]+?)\|\|/; const PATH_REGEX = { mastodon: /^\/@(.+?)\/(\d+)\/?/, mastodon2: /^\/(.+?)\/statuses\/\d+\/?/, - pleroma: - /^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/, + pleroma: /^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/, pleroma2: /^\/notice\/[A-Za-z0-9]+\/?/, misskey: /^\/notes\/[a-z0-9]+\/?/, gotosocial: /^\/@(.+?)\/statuses\/[0-9A-Z]+\/?/, @@ -76,10 +73,7 @@ async function resolvePlatform(url) { }).then((res) => res.json()); if (!nodeinfo?.software?.name) { - logger.error( - "fedimbed", - `Got nodeinfo for "${urlObj.hostname}", but missing software name.` - ); + logger.error("fedimbed", `Got nodeinfo for "${urlObj.hostname}", but missing software name.`); domainCache.set(urlObj.hostname, null); return null; } @@ -89,9 +83,7 @@ async function resolvePlatform(url) { } const keyId = "https://hf.c7.pm/actor#main-key"; -const privKey = fs.readFileSync( - path.resolve(__dirname, "../../priv/private.pem") -); +const privKey = fs.readFileSync(path.resolve(__dirname, "../../priv/private.pem")); async function signedFetch(url, options) { const urlObj = new URL(url); @@ -122,36 +114,6 @@ async function signedFetch(url, options) { return await fetch(url, options); } -function htmlToMarkdown(str) { - // FIXME: stop being lazy and use an html parser - str = str.replace(/(.+?)<\/a>/gi, (_, url, text) => - url == text ? url : `[${text}](${url})` - ); - str = str.replace( - //gi, - "[$3]($1)" - ); - str = str.replace(/<\/?\s*br\s*\/?>/gi, "\n"); - str = str.replace( - /((.|\n)*?)<\/blockquote>/gi, - (_, quote) => "> " + quote.split("\n").join("\n> ") - ); - str = str.replace(/<\/p>

/gi, "\n\n"); - str = str.replace(/

    /gi, "\n"); - str = str.replace(/
  1. /gi, "- "); - str = str.replace(/<\/li>/gi, "\n"); - str = str.replace(/<\/?code>/gi, "`"); - str = str.replace(/<\/?em>/gi, "*"); - str = str.replace(/<\/?u>/gi, "__"); - str = str.replace(/<\/?s>/gi, "~~"); - str = str.replace(/(<([^>]+)>)/gi, ""); - str = parseHtmlEntities(str); - // whyyyyyyyyyyyy - str = str.replace(/\[https?:\/\/.+?\]\((https?:\/\/.+?)\)/gi, "$1"); - - return str; -} - async function processUrl(msg, url, spoiler = false) { let invalidUrl = false; let urlObj; @@ -200,10 +162,7 @@ async function processUrl(msg, url, spoiler = false) { }, }).then((res) => res.text()); } catch (err) { - logger.error( - "fedimbed", - `Failed to signed fetch "${url}", retrying unsigned: ${err}` - ); + logger.error("fedimbed", `Failed to signed fetch "${url}", retrying unsigned: ${err}`); } if (!rawPostData) { try { @@ -223,10 +182,7 @@ async function processUrl(msg, url, spoiler = false) { try { postData = JSON.parse(rawPostData); } catch (err) { - logger.error( - "fedimbed", - `Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"` - ); + logger.error("fedimbed", `Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`); } } else { logger.warn("fedimbed", `Got non-JSON for "${url}": ${rawPostData}`); @@ -280,18 +236,13 @@ async function processUrl(msg, url, spoiler = false) { options.body = JSON.stringify({noteId}); headers["Content-Type"] = "application/json"; } else { - logger.error( - "fedimbed", - `Missing MastoAPI replacement for "${platform}"` - ); + logger.error("fedimbed", `Missing MastoAPI replacement for "${platform}"`); } if (redirUrl) { logger.verbose( "fedimbed", - `Redirecting "${url}" to "${redirUrl}": ${JSON.stringify( - options - )}, ${JSON.stringify(headers)}` + `Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(options)}, ${JSON.stringify(headers)}` ); let rawPostData2; try { @@ -304,10 +255,7 @@ async function processUrl(msg, url, spoiler = false) { }) ).then((res) => res.text()); } catch (err) { - logger.error( - "fedimbed", - `Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}` - ); + logger.error("fedimbed", `Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`); } if (!rawPostData2) { try { @@ -320,10 +268,7 @@ async function processUrl(msg, url, spoiler = false) { }) ).then((res) => res.text()); } catch (err) { - logger.error( - "fedimbed", - `Failed to fetch "${url}" via MastoAPI: ${err}` - ); + logger.error("fedimbed", `Failed to fetch "${url}" via MastoAPI: ${err}`); } } @@ -331,27 +276,18 @@ async function processUrl(msg, url, spoiler = false) { if (rawPostData2?.startsWith("{")) { postData2 = JSON.parse(rawPostData2); } else { - logger.warn( - "fedimbed", - `Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}` - ); + logger.warn("fedimbed", `Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`); } if (!postData2) { - logger.warn( - "fedimbed", - `Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.` - ); + logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`); } else if (postData2.error) { logger.error( "fedimbed", - `Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify( - postData2.error - )}` + `Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(postData2.error)}` ); } else { - cw = - postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw; + cw = postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw; content = postData2.akkoma?.source?.content ?? postData2.pleroma?.content?.["text/plain"] ?? @@ -364,21 +300,12 @@ async function processUrl(msg, url, spoiler = false) { postData2.user?.name ?? postData2.user?.username, handle: - postData2.account?.fqn ?? - `${postData2.account?.username ?? postData2.user?.username}@${ - urlObj.hostname - }`, - url: - postData2.account?.url ?? - `${urlObj.origin}/@${ - postData2.account?.username ?? postData2.user?.username - }`, + postData2.account?.fqn ?? `${postData2.account?.username ?? postData2.user?.username}@${urlObj.hostname}`, + url: postData2.account?.url ?? `${urlObj.origin}/@${postData2.account?.username ?? postData2.user?.username}`, avatar: postData2.account?.avatar ?? postData2.user?.avatarUrl, }; timestamp = postData2.created_at ?? postData2.createdAt; - emotes = postData2.emojis - .filter((x) => !x.name.endsWith("@.")) - .map((x) => ({name: `:${x.name}:`, url: x.url})); + emotes = postData2.emojis.filter((x) => !x.name.endsWith("@.")).map((x) => ({name: `:${x.name}:`, url: x.url})); sensitive = postData2.sensitive; const attachments = postData2.media_attachments ?? postData2.files; @@ -465,15 +392,12 @@ async function processUrl(msg, url, spoiler = false) { if (realUrlObj.origin != urlObj.origin) { platform = await resolvePlatform(postData.id); color = PLATFORM_COLORS[platform]; - platformName = platform - .replace("gotosocial", "GoToSocial") - .replace(/^(.)/, (_, c) => c.toUpperCase()); + platformName = platform.replace("gotosocial", "GoToSocial").replace(/^(.)/, (_, c) => c.toUpperCase()); url = postData.id; } } - content = - postData._misskey_content ?? postData.source?.content ?? postData.content; + content = postData._misskey_content ?? postData.source?.content ?? postData.content; cw = postData.summary; timestamp = postData.published; sensitive = postData.sensitive; @@ -482,36 +406,29 @@ async function processUrl(msg, url, spoiler = false) { let tag = postData.tag; // gts moment if (!Array.isArray(tag)) tag = [tag]; - emotes = tag - .filter((x) => !!x.icon) - .map((x) => ({name: x.name, url: x.icon.url})); + emotes = tag.filter((x) => !!x.icon).map((x) => ({name: x.name, url: x.icon.url})); } // NB: gts doesnt send singular attachments as array - const attachments = Array.isArray(postData.attachment) - ? postData.attachment - : [postData.attachment]; + const attachments = Array.isArray(postData.attachment) ? postData.attachment : [postData.attachment]; for (const attachment of attachments) { if (attachment.mediaType) { if (attachment.mediaType.startsWith("video/")) { videos.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: attachment.mediaType, }); } else if (attachment.mediaType.startsWith("image/")) { images.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: attachment.mediaType, }); } else if (attachment.mediaType.startsWith("audio/")) { audios.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: attachment.mediaType, }); } @@ -524,22 +441,19 @@ async function processUrl(msg, url, spoiler = false) { if (contentType.startsWith("image/")) { images.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: contentType, }); } else if (contentType.startsWith("video/")) { videos.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: contentType, }); } else if (contentType.startsWith("audio/")) { audios.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: contentType, }); } @@ -551,39 +465,29 @@ async function processUrl(msg, url, spoiler = false) { ? type : type + "/" + - (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" - ? "png" - : type == "video" - ? "mp4" - : "mpeg"); + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg"); if (type.startsWith("image")) { images.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: fileType, }); } else if (type.startsWith("video")) { videos.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: fileType, }); } else if (type.startsWith("audio")) { audios.push({ url: attachment.url, - desc: - attachment.name ?? attachment.description ?? attachment.comment, + desc: attachment.name ?? attachment.description ?? attachment.comment, type: fileType, }); } } } else { - logger.warn( - "fedimbed", - `Unhandled attachment structure! ${JSON.stringify(attachment)}` - ); + logger.warn("fedimbed", `Unhandled attachment structure! ${JSON.stringify(attachment)}`); } } @@ -599,30 +503,23 @@ async function processUrl(msg, url, spoiler = false) { images.push({ url: postData.image.url, desc: "", - type: - contentType ?? - "image/" + - imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1), + type: contentType ?? "image/" + imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1), }); } if (postData.name) title = postData.name; // Author data is not sent with the post with AS2 - const authorData = await signedFetch( - postData.actor ?? postData.attributedTo, - { - headers: { - "User-Agent": FRIENDLY_USERAGENT, - Accept: "application/activity+json", - }, - } - ) + const authorData = await signedFetch(postData.actor ?? postData.attributedTo, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: "application/activity+json", + }, + }) .then((res) => res.json()) .catch((err) => { // only posts can be activity+json right now, reduce log spam - if (platform !== "cohost") - logger.error("fedimbed", `Failed to get author for "${url}": ${err}`); + if (platform !== "cohost") logger.error("fedimbed", `Failed to get author for "${url}": ${err}`); }); if (authorData) { @@ -637,9 +534,7 @@ async function processUrl(msg, url, spoiler = false) { // bootleg author, mainly for cohost const authorUrl = postData.actor ?? postData.attributedTo; const authorUrlObj = new URL(authorUrl); - const name = authorUrlObj.pathname.substring( - authorUrlObj.pathname.lastIndexOf("/") + 1 - ); + const name = authorUrlObj.pathname.substring(authorUrlObj.pathname.lastIndexOf("/") + 1); author = { name, handle: `${name}@${authorUrlObj.hostname}`, @@ -661,10 +556,7 @@ async function processUrl(msg, url, spoiler = false) { // We could just continue without author but it'd look ugly and be confusing. if (!author) { - logger.warn( - "fedimbed", - `Bailing trying to re-embed "${url}": Failed to get author.` - ); + logger.warn("fedimbed", `Bailing trying to re-embed "${url}": Failed to get author.`); return {}; } @@ -682,12 +574,7 @@ async function processUrl(msg, url, spoiler = false) { let desc = ""; let MAX_LENGTH = 3999; - if ( - (cw != "" || sensitive) && - images.length == 0 && - videos.length == 0 && - audios.length == 0 - ) { + if ((cw != "" || sensitive) && images.length == 0 && videos.length == 0 && audios.length == 0) { desc += "||" + content + "||"; MAX_LENGTH -= 4; @@ -708,9 +595,7 @@ async function processUrl(msg, url, spoiler = false) { } } - const user = author.name - ? `${author.name} (${author.handle})` - : author.handle; + const user = author.name ? `${author.name} (${author.handle})` : author.handle; const baseEmbed = { color, @@ -734,9 +619,7 @@ async function processUrl(msg, url, spoiler = false) { }; if (images.length > 0) { if (images.length > 1) { - const links = images - .map((attachment, index) => `[Image ${index + 1}](${attachment.url})`) - .join(" | "); + const links = images.map((attachment, index) => `[Image ${index + 1}](${attachment.url})`).join(" | "); if (links.length <= 1024) baseEmbed.fields.push({ @@ -756,9 +639,7 @@ async function processUrl(msg, url, spoiler = false) { if (videos.length > 1) { baseEmbed.fields.push({ name: "Videos", - value: videos - .map((attachment, index) => `[Video ${index + 1}](${attachment.url})`) - .join(" | "), + value: videos.map((attachment, index) => `[Video ${index + 1}](${attachment.url})`).join(" | "), inline: true, }); } else { @@ -773,9 +654,7 @@ async function processUrl(msg, url, spoiler = false) { if (audios.length > 1) { baseEmbed.fields.push({ name: "Audios", - value: audios - .map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`) - .join(" | "), + value: audios.map((attachment, index) => `[Audio ${index + 1}](${attachment.url})`).join(" | "), inline: true, }); } else { @@ -796,29 +675,24 @@ async function processUrl(msg, url, spoiler = false) { const percent = o.count / poll.total; const bar = Math.round(percent * 30); - return `**${o.name}** (${o.count}, ${Math.round( - percent * 100 - )}%)\n\`[${"=".repeat(bar)}${" ".repeat(30 - bar)}]\``; + return `**${o.name}** (${o.count}, ${Math.round(percent * 100)}%)\n\`[${"=".repeat(bar)}${" ".repeat( + 30 - bar + )}]\``; }) - .join("\n\n") + - `\n\n${poll.total} votes \u2022 Ends `, + .join("\n\n") + `\n\n${poll.total} votes \u2022 Ends `, }); } let sendWait = false; if (videos.length > 0 || audios.length > 0 || images.length > 4) { sendWait = true; - if (msg instanceof Dysnomia.Message) await msg.addReaction("\uD83D\uDCE4"); + if (msg instanceof Message) await msg.addReaction("\uD83D\uDCE4"); } const embeds = []; const files = []; - const guild = - msg.channel?.guild ?? - (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false); + const guild = msg.channel?.guild ?? (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false); if (images.length > 0) { if (images.length <= 4) { @@ -852,9 +726,7 @@ async function processUrl(msg, url, spoiler = false) { (cw != "" || spoiler ? "SPOILER_" : "") + (attachment.type.indexOf("/") > -1 ? attachment.type.replace("/", ".") - : attachment.type + - "." + - (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")), + : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")), file, description: attachment.desc, }); @@ -886,9 +758,7 @@ async function processUrl(msg, url, spoiler = false) { (cw != "" || spoiler ? "SPOILER_" : "") + (attachment.type.indexOf("/") > -1 ? attachment.type.replace("/", ".") - : attachment.type + - "." + - (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")), + : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "png")), file, description: attachment.desc, }); @@ -959,9 +829,7 @@ async function processUrl(msg, url, spoiler = false) { (cw != "" || spoiler ? "SPOILER_" : "") + (attachment.type.indexOf("/") > -1 ? attachment.type.replace("/", ".").replace("quicktime", "mov") - : attachment.type + - "." + - (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")), + : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp4")), file, }); } @@ -989,14 +857,8 @@ async function processUrl(msg, url, spoiler = false) { filename: (cw != "" || spoiler ? "SPOILER_" : "") + (attachment.type.indexOf("/") > -1 - ? attachment.type - .replace("/", ".") - .replace("mpeg", "mp3") - .replace("vnd.wave", "wav") - .replace("x-", "") - : attachment.type + - "." + - (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")), + ? attachment.type.replace("/", ".").replace("mpeg", "mp3").replace("vnd.wave", "wav").replace("x-", "") + : attachment.type + "." + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? "mp3")), file, }); } @@ -1006,8 +868,7 @@ async function processUrl(msg, url, spoiler = false) { return { response: { content: - cw != "" && - (images.length > 0 || videos.length > 0 || audios.length > 0) + cw != "" && (images.length > 0 || videos.length > 0 || audios.length > 0) ? `:warning: ${cw} || ${url} ||` : spoiler ? `|| ${url} ||` @@ -1051,10 +912,7 @@ events.add("messageCreate", "fedimbed", async function (msg) { for (const service of Object.keys(PATH_REGEX)) { const regex = PATH_REGEX[service]; if (urlObj && regex.test(urlObj.pathname)) { - logger.verbose( - "fedimbed", - `Hit "${service}" for "${url}", processing now.` - ); + logger.verbose("fedimbed", `Hit "${service}" for "${url}", processing now.`); try { const {response, sendWait} = await processUrl(msg, url, hasSpoiler); await msg.channel.createMessage(response).then(() => { @@ -1067,10 +925,7 @@ events.add("messageCreate", "fedimbed", async function (msg) { } }); } catch (err) { - logger.error( - "fedimbed", - `Error processing "${url}":\n` + err.stack - ); + logger.error("fedimbed", `Error processing "${url}":\n` + err.stack); } break; } @@ -1080,8 +935,7 @@ events.add("messageCreate", "fedimbed", async function (msg) { }); const fedimbedCommand = new InteractionCommand("fedimbed"); -fedimbedCommand.helpText = - "Better embeds for fediverse (Mastodon, Pleroma, etc) posts"; +fedimbedCommand.helpText = "Better embeds for fediverse (Mastodon, Pleroma, etc) posts"; fedimbedCommand.options.url = { name: "url", type: ApplicationCommandOptionTypes.STRING, @@ -1098,8 +952,8 @@ fedimbedCommand.options.spoiler = { }; fedimbedCommand.permissions = Permissions.embedLinks | Permissions.attachFiles; fedimbedCommand.callback = async function (interaction) { - let url = getOption(interaction, fedimbedCommand, "url"); - const spoiler = getOption(interaction, fedimbedCommand, "spoiler"); + let url = this.getOption(interaction, "url"); + const spoiler = this.getOption(interaction, "spoiler"); url = url .replace(/\|/g, "") diff --git a/src/modules/foxwells.js b/src/modules/foxwells.js index db0a512..fdeaff8 100644 --- a/src/modules/foxwells.js +++ b/src/modules/foxwells.js @@ -5,16 +5,15 @@ const CATEGORY = "misc"; const FOXWELLS_GUILD_ID = "300436792916836352"; const {tinycolor} = require("@ctrl/tinycolor"); -const {pastelize} = require("../lib/utils.js"); +const {pastelize} = require("../util/misc.js"); +const {createBoardMessage} = require("../util/starboard.js"); const logger = require("../lib/logger.js"); // taken from rot13.com function rot(s, i) { return s.replace(/[a-zA-Z]/g, function (c) { - return String.fromCharCode( - (c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + i) ? c : c - 26 - ); + return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + i) ? c : c - 26); }); } @@ -42,9 +41,7 @@ utsuholights.callback = async function (msg, line, [hex, bri]) { cachedLightsURL = Buffer.from(rot(LIGHTS_URL, 13), "base64").toString(); // Wow! It's That Easy! } - const response = await fetch( - `${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}` - ); + const response = await fetch(`${cachedLightsURL}?r=${r}&g=${g}&b=${b}${bri ? `&bri=${bri}` : ""}`); if (response.status == 200) { return {reaction: "\uD83D\uDC4C"}; @@ -54,17 +51,11 @@ utsuholights.callback = async function (msg, line, [hex, bri]) { }; //hf.registerCommand(utsuholights); -const JPEG_HEADER = Buffer.from([ - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, -]); +const JPEG_HEADER = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46]); async function fetchPlant() { const res = await fetch("https://nuclear.utsuho.rocks/plant/"); - const boundary = res.headers - .get("Content-Type") - .split(";")[1] - .trim() - .replace("boundary=", "--"); + const boundary = res.headers.get("Content-Type").split(";")[1].trim().replace("boundary=", "--"); let buffer = Buffer.alloc(0); let searchForNextBoundary = false; @@ -78,9 +69,7 @@ async function fetchPlant() { } else if (searchForNextBoundary) { if (data.toString().startsWith(boundary)) { res.body.end(); - const length = Number( - buffer.toString().match(/Content-Length:.*?(\d+)/)[1] - ); + const length = Number(buffer.toString().match(/Content-Length:.*?(\d+)/)[1]); const headerOffset = buffer.indexOf(JPEG_HEADER); const data = buffer.slice(headerOffset, headerOffset + length); @@ -169,127 +158,6 @@ function deleteBoardEntry(id) { }); } -async function findSuitableImage(msg) { - const out = {}; - - const attachments = [...msg.attachments.values()]; - const attachment = attachments[0]; - - if (attachment) { - const url = attachment.url; - const parsed = new URL(url); - if (/(jpe?g|png|gif|webp)$/.test(parsed.pathname)) { - out.url = url; - } else if (/(mp4|webm|mov)$/.test(parsed.pathname)) { - out.video = true; - out.attachment = true; - out.url = "attachment://thumb.jpg"; - - let thumbUrl = attachment.proxyURL; - if (thumbUrl.includes("media.discordapp.net") && thumbUrl.includes("?")) { - if (thumbUrl.endsWith("&")) { - thumbUrl = thumbUrl += "format=jpeg"; - } else { - thumbUrl = thumbUrl += "&format=jpeg"; - } - } else { - thumbUrl = thumbUrl += "?format=jpeg"; - } - - out.file = await fetch(thumbUrl) - .then((res) => res.arrayBuffer()) - .then((buf) => Buffer.from(buf)); - } - } else { - for (const embed of msg.embeds) { - if (!embed.url) continue; - const parsed = new URL(embed.url); - if (embed.url && /(jpe?g|png|gif|webp)$/.test(parsed.pathname)) { - out.url = embed.url; - } else if (embed.image) { - out.url = embed.image.url; - break; - } else if (embed.video) { - out.video = true; - out.url = "attachment://thumb.jpg"; - - let thumbUrl = - embed.video.proxyURL ?? - embed.video.url.replace("cdn.discordapp.com", "media.discordapp.net"); - if ( - thumbUrl.includes("media.discordapp.net") && - thumbUrl.includes("?") - ) { - if (thumbUrl.endsWith("&")) { - thumbUrl = thumbUrl += "format=jpeg"; - } else { - thumbUrl = thumbUrl += "&format=jpeg"; - } - } else { - thumbUrl = thumbUrl += "?format=jpeg"; - } - if (embed.thumbnail?.url) thumbUrl = embed.thumbnail.url; - - let valid = await fetch(thumbUrl, {method: "HEAD"}).then( - (res) => res.ok - ); - if (!valid && thumbUrl.includes("media.discordapp.net")) { - thumbUrl = await hf.bot.requestHandler - .request("POST", "/attachments/refresh-urls", true, { - attachment_urls: [thumbUrl], - }) - .then((res) => res.refreshed_urls[0].refreshed); - valid = true; - } - - if (valid) - out.file = await fetch(thumbUrl) - .then((res) => res.arrayBuffer()) - .then((buf) => Buffer.from(buf)); - break; - } - } - } - - return out; -} - -async function createBoardMessage(msg, count, fetchAttachment = true) { - const name = msg.member?.nick ?? msg.author.globalName ?? msg.author.username; - const embed = { - title: `${count} \u2b50 - ${msg.jumpLink}`, - color: pastelize(name), - description: msg.content, - timestamp: new Date(msg.timestamp).toISOString(), - }; - - let image; - if (fetchAttachment) { - image = await findSuitableImage(msg); - if (image.url) { - if (image.video) { - embed.description += `\n(contains video ${ - image.attachment ? "attachment" : "embed" - })`; - } - embed.image = { - url: image.url, - }; - } - } - - return { - avatarURL: msg.member?.avatarURL ?? msg.author.avatarURL, - username: name, - threadID: VINBOARD_THREAD_ID, - embeds: [embed], - attachments: image?.file - ? [{file: image.file, filename: "thumb.jpg"}] - : null, - wait: true, - }; -} - let vinboard_webhook; let vin_channel; let board_channel; @@ -300,9 +168,7 @@ async function processReaction(_msg, reaction, user) { if (reaction.name != "\u2b50") return; if (!vin_channel) { - vin_channel = hf.bot.guilds - .get(FOXWELLS_GUILD_ID) - .channels.get(VINBOARD_CHANNEL_ID); + vin_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).channels.get(VINBOARD_CHANNEL_ID); } if (!vin_channel) { @@ -311,9 +177,7 @@ async function processReaction(_msg, reaction, user) { } if (!board_channel) { - board_channel = hf.bot.guilds - .get(FOXWELLS_GUILD_ID) - .threads.get(VINBOARD_THREAD_ID); + board_channel = hf.bot.guilds.get(FOXWELLS_GUILD_ID).threads.get(VINBOARD_THREAD_ID); } if (!board_channel) { @@ -321,15 +185,11 @@ async function processReaction(_msg, reaction, user) { return; } - const msg = - vin_channel.messages.get(_msg.id) ?? - (await vin_channel.getMessage(_msg.id)); + const msg = vin_channel.messages.get(_msg.id) ?? (await vin_channel.getMessage(_msg.id)); if (!vinboard_webhook) { const webhooks = await vin_channel.getWebhooks(); - vinboard_webhook = webhooks.find( - (webhook) => webhook.id == VINBOARD_WEBHOOK_ID - ); + vinboard_webhook = webhooks.find((webhook) => webhook.id == VINBOARD_WEBHOOK_ID); } if (!vinboard_webhook) { @@ -338,9 +198,7 @@ async function processReaction(_msg, reaction, user) { } const reacts = await msg.getReaction("\u2b50"); - const trueCount = reacts.filter( - (reactor) => reactor.id != msg.author.id - ).length; + const trueCount = reacts.filter((reactor) => reactor.id != msg.author.id).length; const dbEntry = await getBoardEntry(msg.id); if (dbEntry) { @@ -360,12 +218,7 @@ async function processReaction(_msg, reaction, user) { board_channel.messages.get(dbEntry.board_id) ?? (await board_channel.getMessage(dbEntry.board_id).catch(() => {})); if (_boardMessage) { - logger.verbose( - "vinboard", - `Updating count for "${msg.id}" (${ - dbEntry.count ?? 0 - } -> ${trueCount})` - ); + logger.verbose("vinboard", `Updating count for "${msg.id}" (${dbEntry.count ?? 0} -> ${trueCount})`); const props = { avatarURL: _boardMessage.author.avatarURL, @@ -374,24 +227,17 @@ async function processReaction(_msg, reaction, user) { embeds: _boardMessage.embeds, wait: true, }; - props.attachments = [..._boardMessage.attachments.values()].map( - (attach) => ({id: attach.id}) - ); + props.attachments = [..._boardMessage.attachments.values()].map((attach) => ({id: attach.id})); props.embeds[0].title = `${trueCount} \u2b50`; props.embeds[0].color = pastelize(msg.author.username); - await hf.bot.editWebhookMessage( - vinboard_webhook.id, - vinboard_webhook.token, - _boardMessage.id, - props - ); + await hf.bot.editWebhookMessage(vinboard_webhook.id, vinboard_webhook.token, _boardMessage.id, props); await setBoardEntry(msg.id, trueCount, _boardMessage.id); } else { logger.verbose("vinboard", `Creating entry for "${msg.id}"`); const boardMessage = await hf.bot.executeWebhook( vinboard_webhook.id, vinboard_webhook.token, - await createBoardMessage(msg, trueCount) + await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID) ); await setBoardEntry(msg.id, trueCount, boardMessage.id); } @@ -400,7 +246,7 @@ async function processReaction(_msg, reaction, user) { const boardMessage = await hf.bot.executeWebhook( vinboard_webhook.id, vinboard_webhook.token, - await createBoardMessage(msg, trueCount) + await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID) ); await setBoardEntry(msg.id, trueCount, boardMessage.id); } @@ -409,7 +255,7 @@ async function processReaction(_msg, reaction, user) { const boardMessage = await hf.bot.executeWebhook( vinboard_webhook.id, vinboard_webhook.token, - await createBoardMessage(msg, trueCount) + await createBoardMessage(msg, trueCount, VINBOARD_THREAD_ID) ); await setBoardEntry(msg.id, trueCount, boardMessage.id); } diff --git a/src/modules/general.js b/src/modules/general.js index 23feced..973d9a5 100644 --- a/src/modules/general.js +++ b/src/modules/general.js @@ -1,7 +1,7 @@ const Command = require("../lib/command.js"); const CATEGORY = "general"; -const {snowflakeToTimestamp} = require("../lib/utils.js"); +const {snowflakeToTimestamp} = require("../util/time.js"); const help = new Command("help"); help.category = CATEGORY; @@ -27,9 +27,7 @@ help.callback = function (msg, line) { for (const cat in sorted) { embed.fields.push({ name: cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1), - value: `${sorted[cat].length} Commands\n\`${ - hf.config.prefix - }help --${cat.toLowerCase()}\``, + value: `${sorted[cat].length} Commands\n\`${hf.config.prefix}help --${cat.toLowerCase()}\``, inline: true, }); } @@ -40,9 +38,7 @@ help.callback = function (msg, line) { if (sorted[cat]) { const embed = { - title: `HiddenPhox Help: Category > ${ - cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1) - }`, + title: `HiddenPhox Help: Category > ${cat.toUpperCase().charAt(0) + cat.toLowerCase().substring(1)}`, fields: [], }; @@ -78,9 +74,7 @@ help.callback = function (msg, line) { fields: [ { name: "Category", - value: - cmd.category.toUpperCase().charAt(0) + - cmd.category.toLowerCase().substring(1), + value: cmd.category.toUpperCase().charAt(0) + cmd.category.toLowerCase().substring(1), inline: true, }, ], @@ -125,9 +119,7 @@ ping.callback = async function (msg) { const msgTimestamp = snowflakeToTimestamp(msg.id); const noncePing = Math.floor(msgTimestamp - nonceTimestamp); - const gateway = hf.bot.shards.get( - hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0 - ).latency; + const gateway = hf.bot.shards.get(hf.bot.guildShardMap[hf.bot.channelGuildMap[msg.channel.id]] || 0).latency; const newMsg = await msg.channel.createMessage({ content: `Pong.\n**RTT:** \`...\`\n**Gateway:** \`${gateway}ms\`${ diff --git a/src/modules/image.js b/src/modules/image.js index 0c517b2..4c73360 100644 --- a/src/modules/image.js +++ b/src/modules/image.js @@ -1,9 +1,9 @@ const Command = require("../lib/command.js"); const CATEGORY = "image"; -const {getImage} = require("../lib/utils.js"); +const {getImage} = require("../util/image.js"); -const dumpyConvert = require("dumpy").convert; +//const dumpyConvert = require("dumpy").convert; const Jimp = require("jimp"); async function createImageCallback(msg, url, callback, filename) { @@ -29,7 +29,7 @@ async function createImageCallback(msg, url, callback, filename) { } } -const dumpy = new Command("dumpy"); +/*const dumpy = new Command("dumpy"); dumpy.category = CATEGORY; dumpy.helpText = "Among Us Dumpy GIF Creator"; dumpy.usage = " [url]"; @@ -37,14 +37,9 @@ dumpy.callback = async function (msg, line, [width, url]) { if (isNaN(parseInt(width))) url = width; width = Math.min(Math.max(isNaN(parseInt(width)) ? 10 : width, 2), 48); - return await createImageCallback( - msg, - url, - async (img) => await dumpyConvert(img, width), - "dumpy.gif" - ); + return await createImageCallback(msg, url, async (img) => await dumpyConvert(img, width), "dumpy.gif"); }; -hf.registerCommand(dumpy); +hf.registerCommand(dumpy);*/ const hooh = new Command("hooh"); hooh.category = CATEGORY; @@ -56,14 +51,7 @@ hooh.callback = async function (msg, line) { line, async (url) => { const img = await Jimp.read(url); - const half1 = img - .clone() - .crop( - 0, - img.bitmap.height / 2, - img.bitmap.width, - img.bitmap.height / 2 - ); + const half1 = img.clone().crop(0, img.bitmap.height / 2, img.bitmap.width, img.bitmap.height / 2); const half2 = half1.clone().mirror(false, true); return await img @@ -86,9 +74,7 @@ woow.callback = async function (msg, line) { line, async (url) => { const img = await Jimp.read(url); - const half1 = img - .clone() - .crop(0, 0, img.bitmap.width, img.bitmap.height / 2); + const half1 = img.clone().crop(0, 0, img.bitmap.width, img.bitmap.height / 2); const half2 = half1.clone().mirror(false, true); return await img @@ -111,9 +97,7 @@ haah.callback = async function (msg, line) { line, async (url) => { const img = await Jimp.read(url); - const half1 = img - .clone() - .crop(0, 0, img.bitmap.width / 2, img.bitmap.height); + const half1 = img.clone().crop(0, 0, img.bitmap.width / 2, img.bitmap.height); const half2 = half1.clone().mirror(true, false); return await img @@ -136,9 +120,7 @@ waaw.callback = async function (msg, line) { line, async (url) => { const img = await Jimp.read(url); - const half1 = img - .clone() - .crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height); + const half1 = img.clone().crop(img.bitmap.width / 2, 0, img.bitmap.width / 2, img.bitmap.height); const half2 = half1.clone().mirror(true, false); return await img @@ -160,9 +142,7 @@ invert.callback = async function (msg, line) { msg, line, async (img) => { - return await Jimp.read(img).then((x) => - x.invert().getBufferAsync(Jimp.MIME_PNG) - ); + return await Jimp.read(img).then((x) => x.invert().getBufferAsync(Jimp.MIME_PNG)); }, "invert.png" ); @@ -178,9 +158,7 @@ flip.callback = async function (msg, line) { msg, line, async (img) => { - return await Jimp.read(img).then((x) => - x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG) - ); + return await Jimp.read(img).then((x) => x.mirror(true, false).getBufferAsync(Jimp.MIME_PNG)); }, "flip.png" ); @@ -196,9 +174,7 @@ flop.callback = async function (msg, line) { msg, line, async (img) => { - return await Jimp.read(img).then((x) => - x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG) - ); + return await Jimp.read(img).then((x) => x.mirror(false, true).getBufferAsync(Jimp.MIME_PNG)); }, "flop.png" ); @@ -215,9 +191,7 @@ jpeg.callback = async function (msg, line) { msg, line, async (img) => { - return await Jimp.read(img).then((x) => - x.quality(1).getBufferAsync(Jimp.MIME_JPEG) - ); + return await Jimp.read(img).then((x) => x.quality(1).getBufferAsync(Jimp.MIME_JPEG)); }, "jpeg.jpg" ); diff --git a/src/modules/misc.js b/src/modules/misc.js index c4a536b..fbf5d9b 100644 --- a/src/modules/misc.js +++ b/src/modules/misc.js @@ -3,16 +3,12 @@ const InteractionCommand = require("../lib/interactionCommand.js"); const logger = require("../lib/logger.js"); const CATEGORY = "misc"; -const {ApplicationCommandOptionTypes} = - require("@projectdysnomia/dysnomia").Constants; -const {librex} = require("../../config.json"); -const {getOption} = require("../lib/interactionDispatcher.js"); -const { - formatTime, - parseHtmlEntities, - formatUsername, - safeString, -} = require("../lib/utils.js"); +const {ApplicationCommandOptionTypes} = require("../util/dconstants.js"); + +const {formatUsername, safeString} = require("../util/misc.js"); +const {formatTime} = require("../util/time.js"); +const {parseHtmlEntities} = require("../util/html.js"); + const GoogleImages = require("google-images"); const {tinycolor, random: randomColor} = require("@ctrl/tinycolor"); const sharp = require("sharp"); @@ -28,15 +24,13 @@ yt.usage = "[search term]"; yt.callback = async function (msg, line) { if (!line) return "Arguments are required."; - const req = await fetch( - `${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos` - ).then((x) => x.json()); + const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) => + x.json() + ); const topVid = req.items[0]; - let out = `**${safeString( - parseHtmlEntities(topVid.title) - )}** | \`${safeString( + let out = `**${safeString(parseHtmlEntities(topVid.title))}** | \`${safeString( parseHtmlEntities(topVid.uploaderName) )}\`\nhttps://youtube.com${topVid.url}\n\n**__See Also:__**\n`; @@ -44,9 +38,7 @@ yt.callback = async function (msg, line) { const vid = req.items[i]; if (!vid) continue; - out += `- **${safeString( - parseHtmlEntities(vid.title) - )}** | By: \`${safeString( + out += `- **${safeString(parseHtmlEntities(vid.title))}** | By: \`${safeString( parseHtmlEntities(vid.uploaderName) )}\` | \n`; } @@ -65,7 +57,7 @@ ytInteraction.options.search = { default: "", }; ytInteraction.callback = async function (interaction) { - const search = getOption(interaction, ytInteraction, "search"); + const search = this.getOption(interaction, "search"); return yt.callback(interaction, search); }; @@ -78,9 +70,9 @@ fyt.usage = "[search term]"; fyt.callback = async function (msg, line) { if (!line) return "Arguments are required."; - const req = await fetch( - `${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos` - ).then((x) => x.json()); + const req = await fetch(`${hf.config.piped_api}/search?q=${encodeURIComponent(line)}&filter=videos`).then((x) => + x.json() + ); const vid = req.items[0]; @@ -100,7 +92,7 @@ fytInteraction.options.search = { default: "", }; fytInteraction.callback = async function (interaction) { - const search = getOption(interaction, fytInteraction, "search"); + const search = this.getOption(interaction, "search"); return fyt.callback(interaction, search); }; @@ -119,9 +111,7 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) { const query = args.join(" "); const req = await fetch( - `http://api.wolframalpha.com/v2/query?input=${encodeURIComponent( - query - )}&appid=LH2K8H-T3QKETAGT3&output=json` + `http://api.wolframalpha.com/v2/query?input=${encodeURIComponent(query)}&appid=LH2K8H-T3QKETAGT3&output=json` ).then((x) => x.json()); const data = req.queryresult.pods; @@ -148,11 +138,9 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) { for (const x in extra) { embed.fields.push({ name: extra[x].title, - value: `[${ - extra[x].subpods[0].plaintext.length > 0 - ? extra[x].subpods[0].plaintext - : "" - }](${extra[x].subpods[0].img.src})`, + value: `[${extra[x].subpods[0].plaintext.length > 0 ? extra[x].subpods[0].plaintext : ""}](${ + extra[x].subpods[0].img.src + })`, inline: true, }); } @@ -173,8 +161,7 @@ wolfram.callback = async function (msg, line, args, {verbose, v}) { } let string = ""; - if (data[1].subpods[0].plaintext.length > 0) - string = safeString(data[1].subpods[0].plaintext); + if (data[1].subpods[0].plaintext.length > 0) string = safeString(data[1].subpods[0].plaintext); let text; if (string.length > 2000 - (6 + safeString(query).length)) { @@ -216,8 +203,8 @@ wolframInteraction.options.verbose = { default: false, }; wolframInteraction.callback = async function (interaction) { - const query = getOption(interaction, wolframInteraction, "query"); - const verbose = getOption(interaction, wolframInteraction, "verbose"); + const query = this.getOption(interaction, "query"); + const verbose = this.getOption(interaction, "verbose"); return wolfram.callback(interaction, query, [query], {verbose}); }; @@ -232,10 +219,7 @@ gimg.callback = async function (msg, line) { if (!line) return "No arguments given."; const images = await imagesClient.search(line, { - safe: - msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") - ? "off" - : "high", + safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high", }); const index = Math.floor(Math.random() * images.length); @@ -250,9 +234,7 @@ gimg.callback = async function (msg, line) { url: image.url, }, footer: { - text: `Image ${Number(index) + 1}/${ - images.length - }. Rerun to get a different image.`, + text: `Image ${Number(index) + 1}/${images.length}. Rerun to get a different image.`, }, }, ], @@ -268,10 +250,7 @@ fimg.callback = async function (msg, line) { if (!line) return "No arguments given."; const images = await imagesClient.search(line, { - safe: - msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") - ? "off" - : "high", + safe: msg.channel.nsfw && !msg.channel?.topic.includes("[no_nsfw]") ? "off" : "high", }); const image = images[0]; @@ -295,22 +274,15 @@ poll.category = CATEGORY; poll.helpText = "Start a poll"; poll.usage = "[topic] [option 1] [option 2] [...option 3-10]"; poll.callback = async function (msg, line, [topic, ...options]) { - if (!line || !topic) - return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"'; + if (!line || !topic) return 'Usage: hf!poll "topic" "option 1" "option 2" "...options 3-10"'; const arrOptions = [...options].slice(0, 10); if (arrOptions.length < 2) return "A minimum of two options are required."; const reactions = []; - let displayString = `**${formatUsername( - msg.author - )}** has started a poll:\n## __${topic}__\n`; + let displayString = `**${formatUsername(msg.author)}** has started a poll:\n## __${topic}__\n`; for (let i = 0; i < arrOptions.length; i++) { - displayString += - (i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) + - ": " + - arrOptions[i] + - "\n"; + displayString += (i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`) + ": " + arrOptions[i] + "\n"; reactions[i] = i === 9 ? "\ud83d\udd1f" : `${i + 1}\u20e3`; } @@ -353,28 +325,20 @@ anonradio.callback = async function () { let playing; try { - playing = await fetch("https://anonradio.net/playing").then((res) => - res.text() - ); + playing = await fetch("https://anonradio.net/playing").then((res) => res.text()); } catch (err) { try { - playing = await fetch("http://anonradio.net/playing").then((res) => - res.text() - ); + playing = await fetch("http://anonradio.net/playing").then((res) => res.text()); } catch (err) { // } } let schedule; try { - schedule = await fetch("https://anonradio.net/schedule/").then((res) => - res.text() - ); + schedule = await fetch("https://anonradio.net/schedule/").then((res) => res.text()); } catch (err) { try { - schedule = await fetch("http://anonradio.net/schedule/").then((res) => - res.text() - ); + schedule = await fetch("http://anonradio.net/schedule/").then((res) => res.text()); } catch (err) { // } @@ -384,9 +348,7 @@ anonradio.callback = async function () { const icecast = await fetch("http://anonradio.net:8010/status-json.xsl") .then((res) => res.text()) - .then((data) => - JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",')) - ); + .then((data) => JSON.parse(data.replace(/"title": - ,/g, '"title":" - ",'))); let lines = schedule.split("\n"); lines = lines.slice(4, lines.length - 2); @@ -418,17 +380,11 @@ anonradio.callback = async function () { hour = hour.slice(0, 2) + ":" + hour.slice(-2); const timestamp = - Date.parse( - `${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC` - ) / 1000; + Date.parse(`${DAYS[targetDay]}, ${currentYear}-${targetMonth}-${targetDateDay} ${hour} UTC`) / 1000; let nameOut = name; - if (time == "Sat 0300") - nameOut = name.replace( - "Open Mic - Anyone can stream", - "Synth Battle Royale" - ); + if (time == "Sat 0300") nameOut = name.replace("Open Mic - Anyone can stream", "Synth Battle Royale"); parsedLines.push({ timestamp, @@ -451,14 +407,9 @@ anonradio.callback = async function () { title = `${liveNow.name} (\`${liveNow.id}\`)`; subtitle = playing; } else { - const [_, current, peakDay, peakMonth, dj, metadata] = playing.match( - /\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/ - ); + const [_, current, peakDay, peakMonth, dj, metadata] = playing.match(/\[(\d+)\/(\d+)\/(\d+)\] \((.+?)\): (.+)/); - if ( - metadata == "https://archives.anonradio.net" || - liveNow.name == "Synth Battle Royale" - ) { + if (metadata == "https://archives.anonradio.net" || liveNow.name == "Synth Battle Royale") { title = `${liveNow.name} (\`${dj}\`)`; } else { title = `${metadata} (\`${dj}\`)`; @@ -468,14 +419,10 @@ anonradio.callback = async function () { let openmicTime = ""; if (liveNow.id == "openmic") { - const streamData = icecast.icestats.source.find( - (src) => src.listenurl == "http://anonradio.net:8010/openmic" - ); + const streamData = icecast.icestats.source.find((src) => src.listenurl == "http://anonradio.net:8010/openmic"); if (streamData && streamData.stream_start_iso8601) { const startTime = new Date(streamData.stream_start_iso8601).getTime(); - openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime( - Date.now() - startTime - )} -\\*-\n`; + openmicTime = `-\\*- OpenMIC DJ has been streaming for ${formatTime(Date.now() - startTime)} -\\*-\n`; } } @@ -507,9 +454,7 @@ shodan.callback = async function (msg, line) { if (!line || line == "") return "Arguments required."; if (!REGEX_IPV4.test(line)) return "Invalid IP address."; - const data = await fetch("https://internetdb.shodan.io/" + line).then((res) => - res.json() - ); + const data = await fetch("https://internetdb.shodan.io/" + line).then((res) => res.json()); if (data.detail) return data.detail; @@ -519,10 +464,7 @@ shodan.callback = async function (msg, line) { fields: [ { name: "Hostnames", - value: - data.hostnames.length > 0 - ? data.hostnames.map((str) => `\`${str}\``).join(" ") - : "None", + value: data.hostnames.length > 0 ? data.hostnames.map((str) => `\`${str}\``).join(" ") : "None", inline: true, }, { @@ -532,26 +474,17 @@ shodan.callback = async function (msg, line) { }, { name: "Tags", - value: - data.tags.length > 0 - ? data.tags.map((str) => `\`${str}\``).join(", ") - : "None", + value: data.tags.length > 0 ? data.tags.map((str) => `\`${str}\``).join(", ") : "None", inline: true, }, { name: "CPEs", - value: - data.cpes.length > 0 - ? data.cpes.map((str) => `\`${str}\``).join(" ") - : "None", + value: data.cpes.length > 0 ? data.cpes.map((str) => `\`${str}\``).join(" ") : "None", inline: true, }, { name: "Vulnerabilities", - value: - data.vulns.length > 0 - ? data.vulns.map((str) => `\`${str}\``).join(" ") - : "None", + value: data.vulns.length > 0 ? data.vulns.map((str) => `\`${str}\``).join(" ") : "None", inline: true, }, ], @@ -596,9 +529,7 @@ generate.callback = async function (msg, line) { const title = `Responses for "${safeString(line)}"`; const out = { - content: `Generated in ${formatTime(Date.now() - start)}${ - retries > 0 ? " with " + retries + " retries" : "" - }`, + content: `Generated in ${formatTime(Date.now() - start)}${retries > 0 ? " with " + retries + " retries" : ""}`, embeds: [], files: images, }; @@ -627,7 +558,7 @@ search.addAlias("google"); search.addAlias("ddg"); search.callback = async function (msg, line, args, {results = 2}) { const query = args.join(" "); - if (!librex) return "LibreX instance not defined."; + if (!hf.config.librex) return "LibreX instance not defined."; if (!query || query == "") return "Search query required."; const encodedQuery = encodeURIComponent(query); @@ -638,9 +569,7 @@ search.callback = async function (msg, line, args, {results = 2}) { if (res.url != url) return res.url; } - const res = await fetch(`${librex}/api.php?q=${encodedQuery}&p=0&t=0`).then( - (res) => res.json() - ); + const res = await fetch(`${hf.config.librex}/api.php?q=${encodedQuery}&p=0&t=0`).then((res) => res.json()); if (res.error?.message) { if (res.error.message.indexOf("No results found.") > -1) { return "Search returned no results."; @@ -649,27 +578,17 @@ search.callback = async function (msg, line, args, {results = 2}) { } } else { const searchResults = Object.values(res) - .filter( - (result) => result.did_you_mean == null && typeof result !== "string" - ) + .filter((result) => result.did_you_mean == null && typeof result !== "string") .splice(0, Number(results)); if (searchResults.length > 0) { let out = `__**Results for \`${safeString(query)}\`**__\n`; for (const result of searchResults) { if (result.special_response) { - out += - "> " + - safeString( - parseHtmlEntities( - result.special_response.response.split("\n").join("\n> ") - ) - ); + out += "> " + safeString(parseHtmlEntities(result.special_response.response.split("\n").join("\n> "))); out += `\n<${encodeURI(result.special_response.source)}>`; } else { - out += `**${safeString( - parseHtmlEntities(result.title) - ).trim()}** - <${encodeURI(result.url)}>`; + out += `**${safeString(parseHtmlEntities(result.title)).trim()}** - <${encodeURI(result.url)}>`; out += `\n> ${safeString(parseHtmlEntities(result.description))}`; } out += "\n\n"; @@ -702,8 +621,8 @@ searchInteraction.options.results = { default: 2, }; searchInteraction.callback = async function (interaction) { - const query = getOption(interaction, searchInteraction, "query"); - const results = getOption(interaction, searchInteraction, "results"); + const query = this.getOption(interaction, "query"); + const results = this.getOption(interaction, "results"); return search.callback(interaction, query, [query], {results}); }; @@ -717,9 +636,7 @@ color.callback = async function (msg, line, args, {truerandom}) { random = false; if (!line || line == "" || args.length == 0) { - color = truerandom - ? tinycolor(Math.floor(Math.random() * 0xffffff)) - : randomColor(); + color = truerandom ? tinycolor(Math.floor(Math.random() * 0xffffff)) : randomColor(); random = true; } @@ -806,14 +723,13 @@ colorInteraction.options.input = { colorInteraction.options.truerandom = { name: "truerandom", type: ApplicationCommandOptionTypes.BOOLEAN, - description: - "Should the random color give a 'true random' color instead of an adjust color", + description: "Should the random color give a 'true random' color instead of an adjust color", required: false, default: false, }; colorInteraction.callback = async function (interaction) { - const input = getOption(interaction, colorInteraction, "input"); - const truerandom = getOption(interaction, colorInteraction, "truerandom"); + const input = this.getOption(interaction, "input"); + const truerandom = this.getOption(interaction, "truerandom"); return color.callback(interaction, input, [input], {truerandom}); }; @@ -849,10 +765,7 @@ const handshake = Buffer.concat([ identPort, writeVarInt(1), ]); -const handshakeWithLength = Buffer.concat([ - writeVarInt(handshake.length), - handshake, -]); +const handshakeWithLength = Buffer.concat([writeVarInt(handshake.length), handshake]); const status = Buffer.concat([writeVarInt(1), writeVarInt(0x0)]); @@ -927,12 +840,7 @@ mcserver.callback = async function (msg, line) { } const dataAsString = totalData.toString().trim(); console.log(dataAsString); - const json = JSON.parse( - dataAsString.slice( - dataAsString.indexOf("{"), - dataAsString.lastIndexOf("}") + 1 - ) - ); + const json = JSON.parse(dataAsString.slice(dataAsString.indexOf("{"), dataAsString.lastIndexOf("}") + 1)); logger.verbose("mcserver", "close", json); clearTimeout(timeout); return resolve(json); @@ -946,22 +854,16 @@ mcserver.callback = async function (msg, line) { } else { await msg.removeReaction("\uD83C\uDFD3"); - const motd = data.description.text.replace( - /\u00a7([a-f0-9k-or])/gi, - (formatting) => { - const ansi = formattingToAnsi[formatting]; - return ansi ? `\x1b[${ansi}m` : ""; - } - ); + const motd = data.description.text.replace(/\u00a7([a-f0-9k-or])/gi, (formatting) => { + const ansi = formattingToAnsi[formatting]; + return ansi ? `\x1b[${ansi}m` : ""; + }); const players = data.players?.sample?.map((player) => player.name) ?? []; const totalPlayers = `(${data.players.online}/${data.players.max})`; let image; if (data.favicon) { - image = Buffer.from( - data.favicon.slice(data.favicon.indexOf(",")), - "base64" - ); + image = Buffer.from(data.favicon.slice(data.favicon.indexOf(",")), "base64"); } return { diff --git a/src/modules/moderation.js b/src/modules/moderation.js index 1bc3144..1c73272 100644 --- a/src/modules/moderation.js +++ b/src/modules/moderation.js @@ -1,7 +1,8 @@ const Command = require("../lib/command.js"); const CATEGORY = "moderation"; -const {lookupUser, formatUsername} = require("../lib/utils.js"); +const {formatUsername} = require("../util/misc.js"); +const {lookupUser} = require("../util/selection.js"); const tidy = new Command("tidy"); tidy.addAlias("prune"); @@ -14,16 +15,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) { if (!showHelp && !msg.guildID) { return "Can only be used in guilds."; } - if ( - !showHelp && - !msg.channel.permissionsOf(msg.author.id).has("manageMessages") - ) { + if (!showHelp && !msg.channel.permissionsOf(msg.author.id).has("manageMessages")) { return "You do not have `Manage Messages` permission."; } - if ( - !showHelp && - !msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages") - ) { + if (!showHelp && !msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")) { return "I do not have `Manage Messages` permission."; } if (msg.author.bot) return "Zero-width space your say command."; @@ -36,10 +31,7 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) { limit: count && parseInt(count) > 0 ? parseInt(count) : 10, }) .then((msgs) => msgs.map((m) => m.id)); - await msg.channel.deleteMessages( - messages, - `Message purge by ${formatUsername(msg.author)}` - ); + await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)}`); return `Deleted ${messages.length} message(s).`; } @@ -53,14 +45,10 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) { before: msg.id, limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10, }) - .then((msgs) => - msgs.filter((m) => m.author.id == user.id).map((m) => m.id) - ); + .then((msgs) => msgs.filter((m) => m.author.id == user.id).map((m) => m.id)); await msg.channel.deleteMessages( messages, - `Message purge by ${formatUsername( - msg.author - )} targeting ${formatUsername(user)}` + `Message purge by ${formatUsername(msg.author)} targeting ${formatUsername(user)}` ); return `Deleted ${messages.length} message(s).`; @@ -73,29 +61,20 @@ tidy.callback = async function (msg, line, [subcommand, count, extra]) { limit: count && parseInt(count) > 0 ? parseInt(count) : 50, }) .then((msgs) => msgs.filter((m) => msg.author.bot).map((m) => m.id)); - await msg.channel.deleteMessages( - messages, - `Message purge by ${formatUsername(msg.author)} targeting bots` - ); + await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)} targeting bots`); return `Deleted ${messages.length} message(s).`; } case "filter": { - if (count.length === 0) - return "Filter must be at least 1 character long."; + if (count.length === 0) return "Filter must be at least 1 character long."; const messages = await msg.channel .getMessages({ before: msg.id, limit: extra && parseInt(extra) > 0 ? parseInt(extra) : 10, }) - .then((msgs) => - msgs.filter((m) => m.content.indexOf(count) > -1).map((m) => m.id) - ); - await msg.channel.deleteMessages( - messages, - `Message purge by ${formatUsername(msg.author)} targeting "${count}"` - ); + .then((msgs) => msgs.filter((m) => m.content.indexOf(count) > -1).map((m) => m.id)); + await msg.channel.deleteMessages(messages, `Message purge by ${formatUsername(msg.author)} targeting "${count}"`); return `Deleted ${messages.length} message(s).`; } diff --git a/src/modules/music.js b/src/modules/music.js index c45f8f3..efc8d36 100644 --- a/src/modules/music.js +++ b/src/modules/music.js @@ -4,23 +4,18 @@ const {Readable} = require("node:stream"); const ffprobe = require("node-ffprobe"); const Command = require("../lib/command.js"); -const { - formatTime, - parseHtmlEntities, - formatUsername, - selectionMessage, -} = require("../lib/utils.js"); + +const {formatUsername} = require("../util/misc.js"); +const {formatTime} = require("../util/time.js"); +const {parseHtmlEntities} = require("../util/html.js"); +const {selectionMessage} = require("../util/selection.js"); const REGEX_YOUTUBE = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/; -const REGEX_YOUTUBE_PLAYLIST = - /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/; +const REGEX_YOUTUBE_PLAYLIST = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/playlist\?list=(.+)$/; const REGEX_YOUTUBE_PLAYLIST_SHORT = /^PL[a-zA-Z0-9-_]{1,32}$/; -const REGEX_SOUNDCLOUD = - /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/; -const REGEX_SOUNDCLOUD_PLAYLIST = - /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/(sets\/.+|likes|tracks)$/; -const REGEX_FILE = - /^(https?:\/\/)?.*\..*\/.+\.(mp3|ogg|flac|wav|webm|mp4|mov|mkv|mod|s3m|it|xm)$/; +const REGEX_SOUNDCLOUD = /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/.+$/; +const REGEX_SOUNDCLOUD_PLAYLIST = /^((https?:\/\/)?(www\.|m\.)?soundcloud\.com\/|sc:).+\/(sets\/.+|likes|tracks)$/; +const REGEX_FILE = /^(https?:\/\/)?.*\..*\/.+\.(mp3|ogg|flac|wav|webm|mp4|mov|mkv|mod|s3m|it|xm)$/; let SOUNDCLOUD_CLIENTID; @@ -40,9 +35,7 @@ async function getSoundcloudClientID() { return SOUNDCLOUD_CLIENTID; } const page = await fetch("https://soundcloud.com").then((res) => res.text()); - const scripts = page - .match(/