const path = require('path'); const fs = require('fs'); const fetch = require('node-fetch'); const { Util: DiscordUtil } = require('discord.js'); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); class Util { constructor() { throw new Error("Class may not be instantiated."); } static paginate(items, page = 1, pageLength = 10) { const maxPage = Math.ceil(items.length / pageLength); if(page < 1) page = 1; if(page > maxPage) page = maxPage; const startIndex = (page - 1) * pageLength; return { items: items.length > pageLength ? items.slice(startIndex, startIndex + pageLength) : items, page, maxPage, pageLength }; } static downloadAsBuffer(source) { return new Promise((resolve, reject) => { fetch(source).then((res) => { if(res.ok) resolve(res.buffer()); else reject(res.statusText); }); }); } static readdirRecursive(directory) { const result = []; (function read(directory) { const files = fs.readdirSync(directory); for(const file of files) { const filePath = path.join(directory, file); if(fs.statSync(filePath).isDirectory()) { read(filePath); } else { result.push(filePath); } } }(directory)); return result; } static wait(ms) { return this.delayFor(ms); } static delayFor(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } static mergeDefault(def, given) { if (!given) return def; for (const key in def) { if (!has(given, key) || given[key] === undefined) { given[key] = def[key]; } else if (given[key] === Object(given[key])) { given[key] = Util.mergeDefault(def[key], given[key]); } } return given; } //Shard Managing static fetchRecommendedShards(token, guildsPerShard = 1000) { if(!token) throw new Error("[util] Token missing."); return fetch("https://discord.com/api/v7/gateway/bot", { method: 'GET', headers: { Authorization: `Bot ${token.replace(/^Bot\s*/iu, '')}` } }).then((res) => { if (res.ok) return res.json(); throw res; }) .then((data) => data.shards * (1000 / guildsPerShard)); } static makePlainError(err) { return { name: err.name, message: err.message, stack: err.stack }; } static escapeMarkdown(text, options) { if (!text) return; return DiscordUtil.escapeMarkdown(text, options); } static get formattingPatterns() { return [ ['\\*{1,3}([^*]*)\\*{1,3}', '$1'], ['_{1,3}([^_]*)_{1,3}', '$1'], ['`{1,3}([^`]*)`{1,3}', '$1'], ['~~([^~])~~', '$1'] ]; } static removeMarkdown(content) { if (!content) throw new Error('Missing content'); this.formattingPatterns.forEach(([pattern, replacer]) => { content = content.replace(new RegExp(pattern, 'gu'), replacer); }); return content; } /** * Sanitise user given regex; escapes unauthorised characters * * @static * @param {string} input * @param {string[]} [allowed=['?', '\\', '(', ')', '|']] * @return {string} The sanitised expression * @memberof Util */ static sanitiseRegex(input, allowed = ['?', '\\', '(', ')', '|']) { if (!input) throw new Error('Missing input'); const reg = new RegExp(`[${this.regChars.filter((char) => !allowed.includes(char)).join('')}]`, 'gu'); return input.replace(reg, '\\$&'); } static get regChars() { return ['.', '+', '*', '?', '\\[', '\\]', '^', '$', '(', ')', '{', '}', '=', '<', '>', '|', ':', '\\\\', '-']; } static duration(seconds) { const { plural } = this; let s = 0, m = 0, h = 0, d = 0, w = 0; s = Math.floor(seconds); m = Math.floor(s/60); s %= 60; h = Math.floor(m/60); m %= 60; d = Math.floor(h/24); h %= 24; w = Math.floor(d/7); d %= 7; return `${w ? `${w} ${plural(w, 'week')} ` : ''}${d ? `${d} ${plural(d, 'day')} ` : ''}${h ? `${h} ${plural(h, 'hour')} ` : ''}${m ? `${m} ${plural(m, 'minute')} ` : ''}${s ? `${s} ${plural(s, 'second')} ` : ''}`.trim(); } static plural(amt, word) { if(amt === 1) return word; return `${word}s`; } } module.exports = Util;