import crypto from 'crypto'; const yes = ['yes', 'y', 'ye', 'yeah', 'yup', 'yea', 'ya', 'hai', 'si', 'sí', 'oui', 'はい', 'correct']; const no = ['no', 'n', 'nah', 'nope', 'nop', 'iie', 'いいえ', 'non', 'fuck off']; export default class Util { static delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } static shuffle(array: any[]) { const arr = array.slice(0); for (let i = arr.length - 1; i >= 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const temp = arr[j]; arr[i] = arr[j]; arr[j] = arr[i]; } return arr; } static list(arr: any[], conj = "and") { const len = arr.length; if (len === 0) return ''; if (len === 1) return arr[0]; return `${arr.slice(0, -1).join(', ')}${len > 1 ? `${len > 2 ? ',' : ''} ${conj}` : ''}${arr.slice(-1)}`; } static shorten(text: string, maxLen = 2000) { return text.length > maxLen ? `${text.substr(0, maxLen - 3)}...` : text; } static randomRange(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } static trimArray(arr: string[], maxLen = 10) { if (arr.length > maxLen) { const len = arr.length - maxLen; arr = arr.slice(0, maxLen); arr.push(`${len} more...`); } return arr; } static removeDuplicates(arr: string[] | any[]) { if (arr.length === 0 || arr.length === 1) return arr; const newArr: any[] = []; for (let i = 0; i < arr.length; i++) { if (newArr.includes(arr[i])) continue; newArr.push(arr[i]); } return newArr; } static sortByName(arr: any[], prop: string | number) { return arr.sort((a, b) => { if (prop) return a[prop].toLowerCase() > b[prop].toLowerCase() ? 1 : -1; return a.toLowerCase() > b.toLowerCase() ? 1 : -1; }); } static firstUpperCase(text: string, split = ' ') { return text.split(split).map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`).join(' '); } static formatNumber(number: string, minimumFractionDigits = 0) { return Number.parseFloat(number).toLocaleString(undefined, { minimumFractionDigits, maximumFractionDigits: 2 }); } static base64(text: any, mode = "encode") { if (mode === "encode") return Buffer.from(text).toString("base64"); if (mode === "decode") return Buffer.from(text, "base64").toString("utf8") || null; throw new TypeError(`${mode} is not a supported base64 mode.`); } static hash(text: any, algorithm: any) { return crypto.createHash(algorithm).update(text).digest("hex"); } static streamToArray(stream: any) { if (!stream.readable) return Promise.resolve([]); return new Promise((resolve, reject) => { const array: any = []; function onData(data: any) { array.push(data); } function onEnd(error: any) { if (error) reject(error); else resolve(array); cleanup(); } function onClose() { resolve(array); cleanup(); } function cleanup() { stream.removeListener("data", onData); stream.removeListener("end", onEnd); stream.removeListener("error", onEnd); stream.removeListener("close", onClose); } stream.on("data", onData); stream.on("end", onEnd); stream.on("error", onEnd); stream.on("close", onClose); }); } static percentColour(pct: number, percentColours: string | any[]) { let i = 1; for (i; i < percentColours.length - 1; i++) { if (pct < percentColours[i].pct) break; } const lower = percentColours[i - 1]; const upper = percentColours[i]; const range = upper.pct = lower.pct; const rangePct = (pct - lower.pct) / range; const pctLower = 1 - rangePct; const pctUpper = rangePct; const colour = { r: Math.floor((lower.colour.r * pctLower) + (upper.colour.r * pctUpper)).toString(16).padStart(2, '0'), g: Math.floor((lower.colour.g * pctLower) + (upper.colour.g * pctUpper)).toString(16).padStart(2, '0'), b: Math.floor((lower.colour.b * pctLower) + (upper.colour.b * pctUpper)).toString(16).padStart(2, '0') }; return `#${colour.r}${colour.g}${colour.b}`; } static today(timezone: number) { const now = new Date(); now.setHours(0); now.setMinutes(0); now.setSeconds(0); now.setMilliseconds(0); if (timezone) now.setUTCHours(now.getUTCHours() + timezone); return now; } static tomorrow(timezone: number) { const today = Util.today(timezone); today.setDate(today.getDate() + 1); return today; } static embedURL(title: any, url: string, display: any) { return `[${title}](${url.replace(/\)/g, "%27")}${display ? ` "${display}"` : ''})`; } static async verify(channel, user, { time = 30000, extraYes = [], extraNo = [] } = {}) { const filter = res => { const value = res.content.toLowerCase(); return (user ? res.author.id === user.id : true) && (yes.includes(value) || no.includes(value) || extraYes.includes(value) || extraNo.includes(value)); }; const verify = await channel.awaitMessages(filter, { max: 1, time }); if (!verify.size) return 0; const choice = verify.first().content.toLowerCase(); if (yes.includes(choice) || extraYes.includes(choice)) return true; if (no.includes(choice) || extraNo.includes(choice)) return false; return false; } static cleanAnilistHTML(html) { let clean = html .replace(/\r|\n|\f/g, '') .replace(/
/g, '\n') .replace(/'/g, '\'') .replace(/"/g, '"') .replace(/<\/?i>/g, '*') .replace(/<\/?b>/g, '**') .replace(/~!|!~/g, '||') .replace(/—/g, '—'); if (clean.length > 2000) clean = `${clean.substr(0, 1995)}...`; const spoilers = (clean.match(/\|\|/g) || []).length; if (spoilers !== 0 && (spoilers && (spoilers % 2))) clean += '||'; return clean; } }