diff --git a/src/lib/table.js b/src/lib/table.js new file mode 100644 index 0000000..48e17a4 --- /dev/null +++ b/src/lib/table.js @@ -0,0 +1,73 @@ +module.exports = class { + constructor(titles) { + this._rows = [titles]; + this._widths = []; + for (let i = 0; i < this._rows.length; i++) { + for (let _i = 0; _i < this._rows[i].length; _i++) { + this._widths.push(this._rows[i][_i].length + 2); + } + } + } + + _updateWidth(row) { + for (const index in row) { + const entry = row[index]; + const width = entry.length + 2; + if (width > this._widths[index]) { + this._widths[index] = width; + } + } + } + + addRow(row) { + this._rows.push(row); + this._updateWidth(row); + } + + render() { + function drawRow(ctx, row, index) { + const columns = []; + + for (let i = 0; i < row.length; i++) { + columns.push(row[i].toString().padEnd(ctx._widths[i])); + } + + return columns; + } + + const toDraw = []; + const queue = this._rows.splice(1); + for (const row in queue) { + const _row = drawRow(this, queue[row]); + toDraw.push(_row.join("|")); + } + + this._updateWidth(this._rows[0]); + + const trows = []; + for (const index in this._rows[0]) { + const field = this._rows[0][index]; + let out = field; + const width = this._widths[index]; + out = out.padEnd(width); + trows.push(out); + } + + const title_row = trows.join("|"); + + let seperator_row = ""; + for (const index in this._widths) { + seperator_row += "-".repeat(this._widths[index]); + if (index != this._widths.length - 1) { + seperator_row += "+"; + } + } + + const drawn = [title_row, seperator_row]; + for (const index in toDraw) { + drawn.push(toDraw[index]); + } + + return drawn.join("\n"); + } +}; diff --git a/src/lib/unicode.js b/src/lib/unicode.js new file mode 100644 index 0000000..a20d3c2 --- /dev/null +++ b/src/lib/unicode.js @@ -0,0 +1,38 @@ +const fetch = require("node-fetch"); + +const mappings = {}; + +async function cacheList() { + const data = await fetch( + "https://unicode.org/Public/UNIDATA/UnicodeData.txt" + ).then((res) => res.text()); + + data + .split("\n") + .map((line) => line.split(";").splice(0, 2)) + .forEach(([character, description]) => { + if (character != "") mappings[character.toLowerCase()] = description; + }); +} + +let getNamesFromString; + +if (!global.____unicode_data) { + cacheList().then(() => { + global.____unicode_data = mappings; + getNamesFromString = function (string) { + const codes = [...string].map((char) => char.codePointAt().toString(16)); + + return codes.map((code) => [ + code.padStart(4, "0"), + global.____unicode_data[code.padStart(4, "0")], + ]); + }; + }); +} + +module.exports = { + data: global.____unicode_data, + cacheList, + getNamesFromString, +}; diff --git a/src/lib/utils.js b/src/lib/utils.js index ef79c0b..2ec0d90 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -1,5 +1,6 @@ const {v3} = require("murmurhash"); const colorcolor = require("colorcolor"); +const fetch = require("node-fetch"); function pastelize(id) { const hue = v3(id) % 360; @@ -19,7 +20,97 @@ function getTopColor(msg, id, fallback = 0x7289da) { 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); + } + }); +} + +async function hastebin(body) { + const res = await fetch(`${hf.config.haste_provider}/documents`, { + method: "POST", + body, + }).then((r) => r.json()); + return res.key; +} + module.exports = { pastelize, getTopColor, + safeString, + formatTime, + readableTime, + isGif, + findLastImage, + hastebin, };