const Eris = require("eris"); const murmurhash = require("murmurhash").v3; const colorcolor = require("colorcolor"); const fetch = require("node-fetch"); function pastelize(id) { const hue = murmurhash(id) % 360; const hex = colorcolor(`hsl(${hue},75%,60%)`, "hex"); return parseInt(hex.substring(1), 16); } function getTopColor(msg, id, fallback = 0x7289da) { if (!msg.channel.guild) return fallback; const roles = msg.channel.guild.members .get(id) .roles.map((role) => msg.channel.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 = seconds / 86400; seconds = seconds % 86400; const hours = seconds / 3600; seconds = seconds % 3600; const minutes = seconds / 60; seconds = seconds % 60; return ( (days !== 0 ? `${days.padStart(2, "0")}:` : "") + (hours !== 0 ? `${hours.padStart(2, "0")}:` : "") + `${minutes.padStart(2, "0")}:${seconds.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).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.length > 0) { img = message.attachments[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 img = await findLastImage(msg, false); if (!str) { if (img) return img; } if (msg.attachments[0] && msg.attachments[0].url) { return msg.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 res.key; } hf.selectionMessages = hf.selectionMessages || new Eris.Collection(); async function selectionMessage(msg, heading, options, timeout = 30000) { let content = `${heading}\n\`\`\`ini\n`; options.slice(0, 20).forEach((value, index) => { content += `[${index + 1}] ${value.display}\n`; }); if (options.length > 20) { content += `; Displaying 20/${options.length} results\n`; } content += "\n[c] Cancel```"; const displayMessage = await msg.channel.createMessage({ content, allowedMentions: { repliedUser: false, }, messageReference: { messageID: msg.id, }, }); return await new Promise((resolve, reject) => { function listener(msg2) { if ( msg2.author.id == msg.author.id && msg2.channel.id == msg.channel.id ) { if (msg2.content == "c") { hf.events.remove("messageCreate", `selection.${msg.id}`); clearTimeout(hf.selectionMessages.get(msg.id)); hf.selectionMessages.remove(msg.id); displayMessage.delete(); if (msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages")) { msg2.delete(); } reject("Canceled"); } else { const number = parseInt(msg2.content); if (!isNaN(number) && number < 21 && number > 0) { hf.events.remove("messageCreate", `selection.${msg.id}`); clearTimeout(hf.selectionMessages.get(msg.id)); hf.selectionMessages.remove(msg.id); displayMessage.delete(); if ( msg.channel.permissionsOf(hf.bot.user.id).has("manageMessages") ) { msg2.delete(); } resolve(options[number - 1].value); } } } } hf.events.add("messageCreate", `selection.${msg.id}`, listener); hf.selectionMessages.set( msg.id, setTimeout(() => { hf.events.remove("messageCreate", `selection.${msg.id}`); hf.selectionMessages.remove(msg.id); displayMessage.delete(); 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.channel.guild) { users = msg.channel.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.indexOf(str.toLowerCase()) > -1) ) { selection.push({ value: user, display: `${user.username}#${user.discriminator}${ user.nick ? ` (${user.nick})` : "" }`, }); } } selection.sort((a, b) => a.display - b.display); if (selection.length == 0) { return "No results"; } else if (selection.length == 1) { return selection[0]; } else { selection.splice(20); try { return await selectionMessage( msg, "Multiple users found, please pick from this list:", selection ); } catch (out) { return out; } } } module.exports = { pastelize, getTopColor, safeString, formatTime, readableTime, isGif, findLastImage, getImage, hastebin, selectionMessage, lookupUser, };