329 lines
8.2 KiB
JavaScript
329 lines
8.2 KiB
JavaScript
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 (/<a?:[a-zA-Z0-9_]*:([0-9]*)>/.test(str)) {
|
|
const id = str.match(/<a?:[a-zA-Z0-9_]*:([0-9]*)>/)[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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
var match;
|
|
|
|
if (entityCode in htmlEntities) {
|
|
return htmlEntities[entityCode];
|
|
/*eslint no-cond-assign: 0*/
|
|
} else if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
|
return String.fromCharCode(parseInt(match[1], 16));
|
|
/*eslint no-cond-assign: 0*/
|
|
} else if ((match = entityCode.match(/^#(\d+)$/))) {
|
|
return String.fromCharCode(~~match[1]);
|
|
} else {
|
|
return entity;
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
pastelize,
|
|
getTopColor,
|
|
safeString,
|
|
formatTime,
|
|
readableTime,
|
|
isGif,
|
|
findLastImage,
|
|
getImage,
|
|
hastebin,
|
|
selectionMessage,
|
|
lookupUser,
|
|
parseHtmlEntities,
|
|
};
|