Advertisement
Guest User

Untitled

a guest
Nov 9th, 2018
240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 100.46 KB | None | 0 0
  1. /*
  2.  
  3. addCommand\((\[.+?\]), (\w+?), (\w+?), (\w+?), function\([\w, ]+?\) {.*\n\t([\s\S]+?)\n},\n\t(".*?"),\n\t(".*?"),\n\t(".*?")\n\)
  4.  
  5. module.exports = Cmds.addCommand({
  6. cmds: \1,
  7.  
  8. requires: {
  9. guild: \4,
  10. loud: false
  11. },
  12.  
  13. desc: \6,
  14.  
  15. args: \7,
  16.  
  17. example: \8,
  18.  
  19. func: (cmd, args, msgObj, speaker, channel, guild) => {
  20. \5
  21. }
  22. })
  23. */
  24.  
  25.  
  26. const FileSys = index.FileSys;
  27. const DateFormat = index.DateFormat;
  28. const Exec = index.Exec;
  29. const Path = index.Path;
  30. const NodeUtil = index.NodeUtil;
  31.  
  32. exports.charLimit = 1999;
  33.  
  34. exports.regexURLPerfect = new RegExp(
  35. '^' +
  36. // protocol identifier
  37. '(?:(?:https?|ftp)://)' +
  38. // user:pass authentication
  39. '(?:\\S+(?::\\S*)?@)?' +
  40. '(?:' +
  41. // IP address exclusion
  42. // private & local networks
  43. '(?!(?:10|127)(?:\\.\\d{1,3}){3})' +
  44. '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' +
  45. '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
  46. // IP address dotted notation octets
  47. // excludes loopback network 0.0.0.0
  48. // excludes reserved space >= 224.0.0.0
  49. // excludes network & broacast addresses
  50. // (first & last IP address of each class)
  51. '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
  52. '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
  53. '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
  54. '|' +
  55. // host name
  56. '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
  57. // domain name
  58. '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
  59. // TLD identifier
  60. '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
  61. // TLD may end with dot
  62. '\\.?' +
  63. ')' +
  64. // port number
  65. '(?::\\d{2,5})?' +
  66. // resource path
  67. '(?:[/?#]\\S*)?' +
  68. '$', 'i');
  69.  
  70. exports.rolePermissions = [
  71. 'CREATE_INSTANT_INVITE',
  72. 'KICK_MEMBERS',
  73. 'BAN_MEMBERS',
  74. 'VIEW_AUDIT_LOG',
  75. 'ADMINISTRATOR',
  76. 'MANAGE_CHANNELS',
  77. 'MANAGE_GUILD',
  78. 'ADD_REACTIONS', // add reactions to messages
  79. 'READ_MESSAGES',
  80. 'SEND_MESSAGES',
  81. 'SEND_TTS_MESSAGES',
  82. 'MANAGE_MESSAGES',
  83. 'EMBED_LINKS',
  84. 'ATTACH_FILES',
  85. 'READ_MESSAGE_HISTORY',
  86. 'MENTION_EVERYONE',
  87. 'USE_EXTERNAL_EMOJIS', // use external emojis
  88. 'CONNECT', // connect to voice
  89. 'SPEAK', // speak on voice
  90. 'MUTE_MEMBERS', // globally mute members on voice
  91. 'DEAFEN_MEMBERS', // globally deafen members on voice
  92. 'MOVE_MEMBERS', // move member's voice channels
  93. 'USE_VAD', // use voice activity detection
  94. 'CHANGE_NICKNAME',
  95. 'MANAGE_NICKNAMES', // change nicknames of others
  96. 'MANAGE_ROLES',
  97. 'MANAGE_WEBHOOKS',
  98. 'MANAGE_EMOJIS',
  99. ];
  100.  
  101. exports.rolePermissionsObj = {
  102. CREATE_INSTANT_INVITE: true,
  103. KICK_MEMBERS: true,
  104. BAN_MEMBERS: true,
  105. VIEW_AUDIT_LOG: true,
  106. ADMINISTRATOR: true,
  107. MANAGE_CHANNELS: true,
  108. MANAGE_GUILD: true,
  109. ADD_REACTIONS: true, // add reactions to messages
  110. READ_MESSAGES: true,
  111. SEND_MESSAGES: true,
  112. SEND_TTS_MESSAGES: true,
  113. MANAGE_MESSAGES: true,
  114. EMBED_LINKS: true,
  115. ATTACH_FILES: true,
  116. READ_MESSAGE_HISTORY: true,
  117. MENTION_EVERYONE: true,
  118. USE_EXTERNAL_EMOJIS: true, // use external emojis
  119. CONNECT: true, // connect to voice
  120. SPEAK: true, // speak on voice
  121. MUTE_MEMBERS: true, // globally mute members on voice
  122. DEAFEN_MEMBERS: true, // globally deafen members on voice
  123. MOVE_MEMBERS: true, // move member's voice channels
  124. USE_VAD: true, // use voice activity detection
  125. CHANGE_NICKNAME: true,
  126. MANAGE_NICKNAMES: true, // change nicknames of others
  127. MANAGE_ROLES: true,
  128. MANAGE_WEBHOOKS: true,
  129. MANAGE_EMOJIS: true,
  130. };
  131.  
  132. exports.textChannelPermissions = [
  133. 'CREATE_INSTANT_INVITE',
  134. 'MANAGE_CHANNEL',
  135. 'ADD_REACTIONS', // add reactions to messages
  136. 'READ_MESSAGES',
  137. 'SEND_MESSAGES',
  138. 'SEND_TTS_MESSAGES',
  139. 'MANAGE_MESSAGES',
  140. 'EMBED_LINKS',
  141. 'ATTACH_FILES',
  142. 'READ_MESSAGE_HISTORY',
  143. 'MENTION_EVERYONE',
  144. 'USE_EXTERNAL_EMOJIS', // use external emojis
  145. 'MANAGE_PERMISSIONS',
  146. 'MANAGE_WEBHOOKS',
  147. ];
  148.  
  149. exports.textChannelPermissionsObj = {
  150. ADD_REACTIONS: true, // add reactions to messages
  151. READ_MESSAGES: true,
  152. SEND_MESSAGES: true,
  153. SEND_TTS_MESSAGES: true,
  154. MANAGE_MESSAGES: true,
  155. EMBED_LINKS: true,
  156. ATTACH_FILES: true,
  157. READ_MESSAGE_HISTORY: true,
  158. MENTION_EVERYONE: true,
  159. USE_EXTERNAL_EMOJIS: true, // use external emojis
  160. CREATE_INSTANT_INVITE: true,
  161. MANAGE_CHANNEL: true,
  162. MANAGE_PERMISSIONS: true,
  163. MANAGE_WEBHOOKS: true,
  164. };
  165.  
  166. exports.voiceChannelPermissions = [
  167. 'CONNECT', // connect to voice
  168. 'SPEAK', // speak on voice
  169. 'MUTE_MEMBERS', // globally mute members on voice
  170. 'DEAFEN_MEMBERS', // globally deafen members on voice
  171. 'MOVE_MEMBERS', // move member's voice channels
  172. 'USE_VAD', // use voice activity detection
  173. 'CREATE_INSTANT_INVITE',
  174. 'MANAGE_CHANNEL',
  175. 'MANAGE_PERMISSIONS',
  176. 'MANAGE_WEBHOOKS',
  177. ];
  178.  
  179. exports.voiceChannelPermissionsObj = {
  180. CONNECT: true, // connect to voice
  181. SPEAK: true, // speak on voice
  182. MUTE_MEMBERS: true, // globally mute members on voice
  183. DEAFEN_MEMBERS: true, // globally deafen members on voice
  184. MOVE_MEMBERS: true, // move member's voice channels
  185. USE_VAD: true, // use voice activity detection
  186. CREATE_INSTANT_INVITE: true,
  187. MANAGE_CHANNEL: true,
  188. MANAGE_PERMISSIONS: true,
  189. MANAGE_WEBHOOKS: true,
  190. };
  191.  
  192. exports.permissionsOrder = {
  193. ADMINISTRATOR: 27,
  194. MANAGE_GUILD: 26,
  195. MANAGE_ROLES: 25,
  196. MANAGE_CHANNELS: 24,
  197. MANAGE_CHANNEL: 24, // Channel
  198. MANAGE_WEBHOOKS: 23,
  199. MANAGE_EMOJIS: 22,
  200. MANAGE_PERMISSIONS: 22, // Channel
  201. VIEW_AUDIT_LOG: 21,
  202. MENTION_EVERYONE: 20,
  203. BAN_MEMBERS: 19,
  204. KICK_MEMBERS: 18,
  205. MOVE_MEMBERS: 17,
  206. DEAFEN_MEMBERS: 16,
  207. MUTE_MEMBERS: 15,
  208. MANAGE_MESSAGES: 14,
  209. MANAGE_NICKNAMES: 13,
  210. USE_EXTERNAL_EMOJIS: 12,
  211. ATTACH_FILES: 11,
  212. SEND_TTS_MESSAGES: 10,
  213. ADD_REACTIONS: 9,
  214. EMBED_LINKS: 8,
  215. CHANGE_NICKNAME: 7,
  216. USE_VAD: 6,
  217. SPEAK: 5,
  218. CONNECT: 4,
  219. CREATE_INSTANT_INVITE: 3,
  220. SEND_MESSAGES: 2,
  221. READ_MESSAGE_HISTORY: 1,
  222. READ_MESSAGES: 0,
  223. };
  224.  
  225. exports.permRating = [
  226. ['ADMINISTRATOR', 100],
  227. ['MANAGE_GUILD', 90],
  228. ['MANAGE_ROLES', 80],
  229. ['MANAGE_CHANNELS', 70],
  230. ['MANAGE_EMOJIS', 60],
  231. ['MENTION_EVERYONE', 50],
  232. ['VIEW_AUDIT_LOG', 50],
  233. ['BAN_MEMBERS', 40],
  234. ['KICK_MEMBERS', 30],
  235. ['MANAGE_MESSAGES', 20],
  236. ['MANAGE_NICKNAMES', 20],
  237. ['MOVE_MEMBERS', 20],
  238. ['ATTACH_FILES', 10],
  239. ['ADD_REACTIONS', 10],
  240. ['SEND_MESSAGES', 10],
  241. ];
  242.  
  243. exports.replaceAll = (str, search, replacement) => str.split(search).join(replacement);
  244.  
  245. function getURLChecker() {
  246. const SCHEME = '[a-z\\d.-]+://';
  247. const IPV4 = '(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])';
  248. const HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+";
  249. const TLD = `(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br
  250. |bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu
  251. |fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq
  252. |ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi
  253. |mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|place|pl|pm|pn
  254. |pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm
  255. |tn|to|tp|trade|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wiki|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f
  256. |xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)`;
  257.  
  258. const HOST_OR_IP = `(?:${HOSTNAME}${TLD}|${IPV4})`;
  259. const PATH = '(?:[;/][^#?<>\\s]*)?';
  260. const QUERY_FRAG = '(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?';
  261. const URI1 = `\\b${SCHEME}[^<>\\s]+`;
  262. const URI2 = `\\b${HOST_OR_IP}${PATH}${QUERY_FRAG}(?!\\w)`;
  263.  
  264. const MAILTO = 'mailto:';
  265. const EMAIL = `(?:${MAILTO})?[a-z0-9!#$%&'*+/=?^_\`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@${HOST_OR_IP}${QUERY_FRAG}(?!\\w)`;
  266.  
  267. const URI_RE = new RegExp(`(?:${URI1}|${URI2}|${EMAIL})`, 'ig');
  268. const SCHEME_RE = new RegExp(`^${SCHEME}`, 'i');
  269.  
  270. const quotes = {
  271. "'": '`',
  272. '>': '<',
  273. ')': '(',
  274. ']': '[',
  275. '}': '{',
  276. '»': '«',
  277. '›': '‹',
  278. };
  279.  
  280. const defaultOptions = {
  281. callback(text, href) {
  282. return href || null;
  283. },
  284. punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/,
  285. };
  286.  
  287. function checkURLs(txtParam, optionsParam) {
  288. let txt = exports.replaceAll(txtParam, '\\', '');
  289. txt = exports.replaceAll(txt, '*', '');
  290. txt = exports.replaceAll(txt, '_', '');
  291.  
  292. if (txt.includes('roblox')) Util.log(txt);
  293.  
  294. const options = optionsParam || {};
  295.  
  296. // Temp variables.
  297. let arr;
  298. let i;
  299. let link;
  300. let href;
  301.  
  302. // Output HTML.
  303. // const html = '';
  304.  
  305. // Store text / link parts, in order, for re-combination.
  306. const parts = [];
  307.  
  308. // Used for keeping track of indices in the text.
  309. let idxPrev;
  310. let idxLast;
  311. let idx;
  312. let linkLast;
  313.  
  314. // Used for trimming trailing punctuation and quotes from links.
  315. let matchesBegin;
  316. let matchesEnd;
  317. let quoteBegin;
  318. let quoteEnd;
  319.  
  320. // Initialize options.
  321. for (i of Object.keys(defaultOptions)) {
  322. if (options[i] == null) {
  323. options[i] = defaultOptions[i];
  324. }
  325. }
  326.  
  327. const inRep = (a) => {
  328. idxLast -= a.length;
  329. return '';
  330. };
  331.  
  332. // Find links.
  333. while (arr = URI_RE.exec(txt)) {
  334. link = arr[0];
  335. idxLast = URI_RE.lastIndex;
  336. idx = idxLast - link.length;
  337.  
  338. // Not a link if preceded by certain characters.
  339. if (/[/:]/.test(txt.charAt(idx - 1))) {
  340. continue;
  341. }
  342.  
  343. // Trim trailing punctuation.
  344. do {
  345. // If no changes are made, we don't want to loop forever!
  346. linkLast = link;
  347.  
  348. quoteEnd = link.substr(-1);
  349. quoteBegin = quotes[quoteEnd];
  350.  
  351. // Ending quote character?
  352. if (quoteBegin) {
  353. matchesBegin = link.match(new RegExp(`\\${quoteBegin}(?!$)`, 'g'));
  354. matchesEnd = link.match(new RegExp(`\\${quoteEnd}`, 'g'));
  355.  
  356. // If quotes are unbalanced, remove trailing quote character.
  357. if ((matchesBegin ? matchesBegin.length : 0) < (matchesEnd ? matchesEnd.length : 0)) {
  358. link = link.substr(0, link.length - 1);
  359. idxLast--;
  360. }
  361. }
  362.  
  363. // Ending non-quote punctuation character?
  364. if (options.punct_regexp) {
  365. link = link.replace(options.punct_regexp, inRep);
  366. }
  367. } while (link.length && link !== linkLast);
  368.  
  369. href = link;
  370.  
  371. // Add appropriate protocol to naked links.
  372. if (!SCHEME_RE.test(href)) {
  373. const origHref = href;
  374. if (href.indexOf('@') != -1) {
  375. if (!href.indexOf(MAILTO)) {
  376. href = '';
  377. } else {
  378. href = MAILTO;
  379. }
  380. } else if (!href.indexOf('irc.')) {
  381. href = 'irc://';
  382. } else if (!href.indexOf('ftp.')) {
  383. href = 'ftp://';
  384. } else {
  385. href = 'http://';
  386. }
  387. href += origHref;
  388. }
  389.  
  390. // Push preceding non-link text onto the array.
  391. if (idxPrev !== idx) {
  392. parts.push([txt.slice(idxPrev, idx)]);
  393. idxPrev = idxLast;
  394. }
  395.  
  396. // Push massaged link onto the array
  397. parts.push([link, href]);
  398. }
  399.  
  400. // Push remaining non-link text onto the array.
  401. parts.push([txt.substr(idxPrev)]);
  402.  
  403. // Process the array items.
  404. const URLs = [];
  405.  
  406. for (i = 0; i < parts.length; i++) {
  407. const result = options.callback.apply('nooone', parts[i]);
  408. if (result) {
  409. URLs.push(result);
  410. }
  411. }
  412.  
  413. return URLs;
  414. }
  415.  
  416. return checkURLs;
  417. }
  418.  
  419. exports.checkURLs = getURLChecker();
  420.  
  421. exports.initRoles = function (sendRole, guild) {
  422. const members = guild.members;
  423.  
  424. members.forEach((member) => {
  425. if (!exports.hasRole(member, sendRole)) {
  426. member.addRole(sendRole)
  427. .then(() => Util.log(`Assigned role to ${exports.getName(member)}`))
  428. .catch(error => Util.log(`[E_InitRoles] addRole: ${error}`));
  429. }
  430. });
  431. };
  432.  
  433. exports.arrayToObj = function (arr) {
  434. const obj = {};
  435. for (let i = 0; i < arr.length; i++) {
  436. const val = arr[i];
  437. obj[val] = true;
  438. }
  439. return obj;
  440. };
  441.  
  442. exports.capitalize = function (strParam) {
  443. let str = strParam;
  444. str = String(str);
  445. return str.charAt(0).toUpperCase() + str.slice(1);
  446. };
  447.  
  448. exports.runLua = function (args, channel) {
  449. // args = "os=nil;io=nil;debug=nil;package=nil;require=nil;loadfile=nil;dofile=nil;collectgarbage=nil;" + args;
  450. const tagNum = Math.floor((new Date()).getTime());
  451. const fileDir = `/tmp/script_${tagNum}.lua`;
  452. FileSys.writeFile(fileDir, args, (err) => {
  453. if (err) {
  454. Util.log(`Script creation error: ${err}`);
  455. Util.print(channel, `Script creation error: ${err}`);
  456. }
  457. Exec(`lua ${fileDir}`, (error, stdoutParam, stderr) => {
  458. let stdout = stdoutParam;
  459. if (!stdout) stdout = '';
  460. const safeOut = Util.safe(stdout);
  461. // var safeErr = Util.safe(stderr);
  462. const outStr = [];
  463. if (error) {
  464. outStr.push('**Execution error:**');
  465. outStr.push('```');
  466. Util.log(`Execution Error: ${stderr}`);
  467. outStr.push(error);
  468. outStr.push('```');
  469. } else {
  470. if (safeOut.length <= 1980) {
  471. outStr.push('**Output:**');
  472. outStr.push('```');
  473. outStr.push(safeOut);
  474. outStr.push('```');
  475. } else {
  476. const options = {
  477. url: 'https://hastebin.com/documents',
  478. method: 'POST',
  479. headers: { 'Content-Type': 'text/plain' },
  480. body: stdout,
  481. };
  482. index.Request(options, (error2, response, bodyParam) => {
  483. const body = JSON.parse(bodyParam);
  484. if (error2 || !body || !body.key) {
  485. Util.print(channel, 'Hastebin upload error:', error2);
  486. } else {
  487. Util.print(channel, 'Output:', `https://hastebin.com/raw/${body.key}`);
  488. }
  489. });
  490. }
  491. if (stderr) {
  492. outStr.push('**Lua Error:**');
  493. outStr.push('```');
  494. Util.log(`Lua Error: ${stderr}`);
  495. outStr.push(stderr);
  496. outStr.push('```');
  497. }
  498. }
  499. Util.print(channel, outStr.join('\n'));
  500. FileSys.unlink(fileDir);
  501. });
  502. });
  503. };
  504.  
  505. exports.doXOR = function (a, b) {
  506. const result = ((a == 1 || b == 1) && !(a == 1 && b == 1)) ? 1 : 0;
  507. return result;
  508. };
  509.  
  510. exports.capitalize2 = function (strParam, repUnder) {
  511. let str = String(strParam);
  512. if (repUnder) str = exports.replaceAll(str, '_', ' ');
  513. str = str.replace(/[0-9a-z]+/ig, (txt) => { Util.log(txt); return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); });
  514. return str;
  515. };
  516.  
  517. exports.boolToAns = function (bool) {
  518. const result = bool ? 'Yes' : 'No';
  519. return result;
  520. };
  521.  
  522. exports.safe = function (str) {
  523. if (typeof (str) === 'string') return str.replace(/`/g, '\\`').replace(/@/g, '@­');
  524. return undefined;
  525. };
  526.  
  527. exports.safe2 = function (str) {
  528. if (typeof (str) === 'string') return str.replace(/`/g, '\\`');
  529. return undefined;
  530. };
  531.  
  532. exports.safeEveryone = function (str) {
  533. if (typeof (str) === 'string') {
  534. const newStr = str.replace(/@everyone/g, '@​everyone');
  535. return newStr.replace(/@here/g, '@​here');
  536. }
  537. return undefined;
  538. };
  539.  
  540. exports.fix = str => (`\`${exports.safe(str)}\``);
  541.  
  542. exports.toFixedCut = (num, decimals) => Number(num.toFixed(decimals)).toString();
  543.  
  544. exports.grabFiles = function (filePath, filter = () => true) {
  545. const dirFiles = FileSys.readdirSync(filePath);
  546. let fullFiles = [];
  547. dirFiles.forEach((file) => {
  548. const fileData = FileSys.lstatSync(`${filePath}${file}`);
  549. if (fileData.isDirectory()) {
  550. const toAdd = exports.grabFiles(`${filePath}${file}/`, filter);
  551. fullFiles = fullFiles.concat(toAdd);
  552. } else if (filter(file)) {
  553. fullFiles.push(`${filePath}${file}`);
  554. }
  555. });
  556. return fullFiles;
  557. };
  558.  
  559. exports.bulkRequire = function (filePath) {
  560. const bulkFiles = exports.grabFiles(filePath, file => file.endsWith('.js'));
  561.  
  562. for (const data of Object.values(bulkFiles)) {
  563. exports.pathRequire(data);
  564. }
  565. };
  566.  
  567. exports.pathRequire = function (filePath) {
  568. const file = Path.resolve(filePath);
  569. delete require.cache[require.resolve(file)];
  570.  
  571. const fileData = require(filePath);
  572.  
  573. const dirName = /(\w+)[/\\]\w+\.js$/.exec(file)[1];
  574.  
  575. if (dirName && has.call(index.commandTypes, dirName)) {
  576. const cmdTypes = index.commandTypes;
  577. for (const [commandType, commandKey] of Object.entries(cmdTypes)) {
  578. if (commandKey !== 'null') {
  579. if (commandType === dirName) {
  580. fileData[2][commandKey] = true;
  581. } else {
  582. fileData[2][commandKey] = false;
  583. }
  584. }
  585. }
  586. }
  587. };
  588.  
  589. exports.checkStaff = function (guild, member) {
  590. if (member.id === vaebId || member.id === selfId || member.id === guild.ownerID) return true;
  591. const speakerRoles = member.roles;
  592. if (!speakerRoles) return false;
  593. // if (exports.getPermRating(guild, member) >= 30) return true;
  594. return speakerRoles.some(role => role.name === 'Staff' || role.name === 'Owner/Seller' || role.name === 'Bot Admin'
  595. || role.name === 'Moderator' || role.name.includes('Head Mod') || role.name === 'Trial Moderator');
  596. };
  597.  
  598. exports.commandFailed = function (channel, speaker, tag, message) {
  599. if (message == null) {
  600. message = tag;
  601. tag = null;
  602. }
  603.  
  604. const tagMessage = tag ? `[${tag}] ` : '';
  605.  
  606. if (channel != null) {
  607. exports.sendEmbed(channel, `${tagMessage}Command Failed`, message, exports.makeEmbedFooter(speaker), null, colGreen, null);
  608. } else {
  609. Util.log(`${tagMessage}[Command_Failed] ${speaker.id}: ${message}`);
  610. }
  611.  
  612. return false;
  613. };
  614.  
  615. exports.getRandomInt = function (minParam, maxParam) { // inclusive, exclusive
  616. maxParam++; // inclusive, inclusive
  617. const min = Math.ceil(minParam);
  618. const max = Math.floor(maxParam);
  619. return Math.floor(Math.random() * (max - min)) + min;
  620. };
  621.  
  622. /* function chunkStringLine(str, size) {
  623. var numChunks = Math.ceil(str.length / size);
  624. var chunks = [];
  625.  
  626. for (var i = 0, o = 0; i < numChunks; ++i, o += size) {
  627. chunks[i] = str.substr(o, size);
  628. }
  629.  
  630. var chunkLength = chunks.length;
  631.  
  632. if (numChunks > 1) {
  633. for (var i = 0; i < chunkLength; i++) {
  634. var nowChunk = chunks[i];
  635. var lastLine = nowChunk.lastIndexOf("\n");
  636. if (lastLine >= 0) {
  637. var nowChunkMsg = nowChunk.substring(0, lastLine);
  638. chunks[i] = nowChunkMsg;
  639. var nextChunkMsg = nowChunk.substring(lastLine+1);
  640. if (chunks[i+1] == null) {
  641. if (nextChunkMsg == "" || nextChunkMsg == "\n" || nextChunkMsg == "```" || nextChunkMsg == "\n```") break;
  642. chunks[i+1] = "";
  643. }
  644. chunks[i+1] = nextChunkMsg + chunks[i+1];
  645. }
  646. }
  647. }
  648.  
  649. return chunks;
  650. } */
  651.  
  652. /*
  653.  
  654. -Chunk string into sets of 2k chars
  655. -For each chunk
  656. -If msg includes newline and first character of next message isn't newline
  657. -Find last newline (unless start of next chunk is newline in which case use the if statement below), where the character before it isn't a codeblock
  658. -Copy everything after the newline to the start of the next chunk
  659. -Set msg to everything before the newline
  660. -If number of code blocks is odd and there are non-whitespace characters after the last codeblock
  661. -Add a codeblock to the end of the chunk
  662. -If number of characters is above 2000
  663. -Find last newline under (or equal) the 2001 character mark, where the character before it isn't a codeblock
  664. -If no newline
  665. -Append a newline as <= 2001st character (not between code blocks if possible)
  666. -Copy everything after the newline (but before the code block), then append it (with an extra newline on the end) to the start of the next chunk
  667. -Cut the chunk to everything before the newline
  668.  
  669. */
  670.  
  671. exports.isObject = function (val) { // Or array
  672. if (val == null) return false;
  673. return (typeof (val) === 'object');
  674. };
  675.  
  676. exports.cloneObj = function (obj, fixBuffer) {
  677. let copy;
  678.  
  679. if (obj == null || typeof (obj) !== 'object') return obj;
  680.  
  681. if (obj instanceof Date) {
  682. copy = new Date();
  683. copy.setTime(obj.getTime());
  684. return copy;
  685. }
  686.  
  687. if (obj instanceof Array) {
  688. copy = [];
  689. const len = obj.length;
  690. for (let i = 0; i < len; i++) {
  691. copy[i] = exports.cloneObj(obj[i], fixBuffer);
  692. }
  693. return copy;
  694. }
  695.  
  696. if (fixBuffer && obj instanceof Buffer) {
  697. return obj.readUIntBE(0, 1);
  698. }
  699.  
  700. if (obj instanceof Object && !(obj instanceof Buffer)) {
  701. copy = {};
  702.  
  703. for (const [attr, objAttr] of Object.entries(obj)) {
  704. copy[attr] = exports.cloneObj(objAttr, fixBuffer);
  705. }
  706. return copy;
  707. }
  708.  
  709. return obj;
  710. };
  711.  
  712. const elapseTimeTags = {};
  713.  
  714. exports.throwErr = function () {
  715. setTimeout(() => {
  716. throw new Error('err');
  717. }, 1000);
  718. };
  719.  
  720. exports.getElapsed = function (tag, remove) {
  721. let elapsed;
  722.  
  723. if (elapseTimeTags[tag] != null) {
  724. const startTimeData = elapseTimeTags[tag];
  725. const elapsedTimeData = process.hrtime(startTimeData); // Seconds, Nanoseconds (Seconds * 1e9)
  726. elapsed = (elapsedTimeData[0] * 1e3) + Number((elapsedTimeData[1] / 1e6).toFixed(3));
  727. }
  728.  
  729. if (remove) {
  730. elapseTimeTags[tag] = null;
  731. delete elapseTimeTags[tag]; // Remove time storage
  732. } else {
  733. elapseTimeTags[tag] = process.hrtime(); // Mark the start time
  734. }
  735.  
  736. return elapsed;
  737. };
  738.  
  739. exports.formatTime = function (time) {
  740. let timeStr;
  741. let formatStr;
  742.  
  743. const numSeconds = exports.round(time / 1000, 0.1);
  744. const numMinutes = exports.round(time / (1000 * 60), 0.1);
  745. const numHours = exports.round(time / (1000 * 60 * 60), 0.1);
  746. const numDays = exports.round(time / (1000 * 60 * 60 * 24), 0.1);
  747. const numWeeks = exports.round(time / (1000 * 60 * 60 * 24 * 7), 0.1);
  748. const numMonths = exports.round(time / (1000 * 60 * 60 * 24 * 30.42), 0.1);
  749. const numYears = exports.round(time / (1000 * 60 * 60 * 24 * 365.2422), 0.1);
  750.  
  751. if (numSeconds < 1) {
  752. timeStr = exports.toFixedCut(time, 0);
  753. formatStr = `${timeStr} millisecond`;
  754. } else if (numMinutes < 1) {
  755. timeStr = exports.toFixedCut(numSeconds, 1);
  756. formatStr = `${timeStr} second`;
  757. } else if (numHours < 1) {
  758. timeStr = exports.toFixedCut(numMinutes, 1);
  759. formatStr = `${timeStr} minute`;
  760. } else if (numDays < 1) {
  761. timeStr = exports.toFixedCut(numHours, 1);
  762. formatStr = `${timeStr} hour`;
  763. } else if (numWeeks < 1) {
  764. timeStr = exports.toFixedCut(numDays, 1);
  765. formatStr = `${timeStr} day`;
  766. } else if (numMonths < 1) {
  767. timeStr = exports.toFixedCut(numWeeks, 1);
  768. formatStr = `${timeStr} week`;
  769. } else if (numYears < 1) {
  770. timeStr = exports.toFixedCut(numMonths, 1);
  771. formatStr = `${timeStr} month`;
  772. } else {
  773. timeStr = exports.toFixedCut(numYears, 1);
  774. formatStr = `${timeStr} year`;
  775. }
  776.  
  777. if (timeStr !== '1') formatStr += 's';
  778.  
  779. return formatStr;
  780. };
  781.  
  782. exports.chunkString = function (str, maxChars) {
  783. const iterations = Math.ceil(str.length / maxChars);
  784. const chunks = new Array(iterations);
  785. for (let i = 0, j = 0; i < iterations; ++i, j += maxChars) chunks[i] = str.substr(j, maxChars);
  786. return chunks;
  787. };
  788.  
  789. exports.cutStringSafe = function (msg, postMsg, lastIsOpener) { // Tries to cut the string along a newline
  790. let lastIndex = msg.lastIndexOf('\n');
  791. if (lastIndex < 0) return [msg, postMsg];
  792. let preCut = msg.substring(0, lastIndex);
  793. let postCut = msg.substring(lastIndex + 1);
  794. const postHasBlock = postCut.includes('```');
  795. if (postHasBlock && !lastIsOpener) { // If postCut is trying to pass over a code block (not allowed) might as well just cut after the code block (as long as it's a closer)
  796. lastIndex = msg.lastIndexOf('```');
  797. preCut = msg.substring(0, lastIndex + 3);
  798. postCut = msg.substring(lastIndex + 3);
  799. } else {
  800. const strEnd1 = preCut.substr(Math.max(preCut.length - 3, 0), 3);
  801. const strEnd2 = preCut.substr(Math.max(preCut.length - 4, 0), 4);
  802. if (postHasBlock || (lastIsOpener && (strEnd1 === '```' || strEnd2 === '``` ' || strEnd2 === '```\n'))) { // If post is triyng to pass over opener or last section of preCut is an opener
  803. return [msg, postMsg];
  804. }
  805. }
  806. return [preCut, postCut + postMsg];
  807. };
  808.  
  809. exports.fixMessageLengthNew = function (msgParam) {
  810. const argsFixed = exports.chunkString(msgParam, exports.charLimit); // Group string into sets of 2k chars
  811. const minusLimit = exports.charLimit - 4;
  812. // argsFixed.forEach(o => Util.log("---\n" + o));
  813. let totalBlocks = 0; // Total number of *user created* code blocks come across so far (therefore if the number is odd then code block is currently open)
  814. for (let i = 0; i < argsFixed.length; i++) {
  815. let passOver = ''; // String to pass over as the start of the next chunk
  816. let msg = argsFixed[i];
  817. const numBlock = (msg.match(/```/g) || []).length; // Number of user created code blocks in this chunk
  818. if (totalBlocks % 2 == 1) msg = `\`\`\`\n${msg}`; // If code block is currently open then this chunk needs to be formatted
  819. totalBlocks += numBlock; // The user created code blocks may close/open new code block (don't need to include added ones because they just account for separate messages)
  820. let lastIsOpener = totalBlocks % 2 == 1; // Checks whether the last code block is an opener or a closer
  821. if (lastIsOpener && msg.length > minusLimit) { // If the chunk ends with the code block still open then it needs to be auto-closed so the chunk needs to be shortened so it can fit
  822. passOver = msg.substring(minusLimit);
  823. msg = msg.substr(0, minusLimit);
  824. const numPass = (passOver.match(/```/g) || []).length; // If we end up passing over code blocks whilst trying to shorten the string, we need to account for the new amount
  825. totalBlocks -= numPass;
  826. if (numPass % 2 == 1) lastIsOpener = false;
  827. }
  828. const nextMsg = passOver + (argsFixed[i + 1] != null ? argsFixed[i + 1] : ''); // Message for next chunk (or empty string if none)
  829. if (nextMsg !== '' && nextMsg[0] !== '\n' && msg.includes('\n')) { // If start of next chunk is a newline then can just leave the split as it is now (same goes for this chunk having no newlines)
  830. const cutData = exports.cutStringSafe(msg, '', lastIsOpener);
  831. msg = cutData[0];
  832. passOver = cutData[1] + passOver;
  833. }
  834. if (lastIsOpener) msg += '\n```'; // Close any left over code blocks (and re open on next chunk if they continue)
  835. argsFixed[i] = msg;
  836. if (passOver.length > 0) { // Whether any text actually needs to be passed
  837. if (argsFixed[i + 1] == null) argsFixed[i + 1] = ''; // Create new chunk if this is the last one
  838. argsFixed[i + 1] = passOver + argsFixed[i + 1];
  839. }
  840. }
  841. return argsFixed;
  842. };
  843.  
  844. /* function fixMessageLength(msg) {
  845. var argsFixed = chunkStringLine(msg, 2000);
  846. var argsLength = argsFixed.length;
  847. for (var i = 0; i < argsFixed.length; i++) {
  848. var passOver = "";
  849. var msg = argsFixed[i];
  850. //Util.log("Original message length: " + msg.length);
  851. if (msg.length > 1996) {
  852. passOver = msg.substring(1996);
  853. msg = msg.substring(0, 1996);
  854. //Util.log("passStart orig: " + passOver.length);
  855. var lastLine = msg.lastIndexOf("\n");
  856. if (lastLine >= 5) {
  857. var msgEnd = lastLine;
  858. var passStart = msgEnd+1;
  859. passOver = msg.substring(passStart) + passOver;
  860. msg = msg.substring(0, msgEnd);
  861. //Util.log("passOver: " + passOver.length);
  862. //Util.log("msg: " + msg.length);
  863. //Util.log("lastLine: " + lastLine);
  864. }
  865. }
  866. var numBlock = (msg.match(/```/g) || []).length;
  867. if (numBlock % 2 == 1) {
  868. passOver = "```\n" + passOver;
  869. msg = msg + "\n```";
  870. }
  871. argsFixed[i] = msg;
  872. //Util.log("Message length: " + msg.length);
  873. //Util.log("Pass Over: " + passOver.length);
  874. if (passOver != "" && (argsFixed[i+1] != null || passOver != "```\n")) {
  875. if (argsFixed[i+1] == null) {
  876. //Util.log("Created new print block extender")
  877. argsFixed[i+1] = "";
  878. }
  879. argsFixed[i+1] = passOver + argsFixed[i+1];
  880. }
  881. }
  882.  
  883. return argsFixed;
  884. } */
  885.  
  886. exports.splitMessagesOld = function (messages) {
  887. const fixed = exports.fixMessageLengthNew(messages.join(' '));
  888. return fixed;
  889. };
  890.  
  891. exports.escapeRegExp = function (str) {
  892. return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
  893. };
  894.  
  895. function getMatchesWithBlock(str, matchChars, blockChars, useInside) { // Gets all matches of a substring that are in/out of a code block
  896. const pattern = new RegExp(exports.escapeRegExp(blockChars), 'g');
  897. let result;
  898.  
  899. let numMatches = 0;
  900. let strPointer = 0;
  901. let newStr = '';
  902.  
  903. while (result = pattern.exec(str)) {
  904. numMatches++;
  905. if (useInside) {
  906. if (numMatches % 2 == 1) { // Open block
  907. newStr += '.'.repeat(result.index - strPointer);
  908. strPointer = result.index;
  909. } else { // Close block (Store data)
  910. newStr += '.'.repeat(blockChars.length) + str.substring(strPointer + blockChars.length, result.index);
  911. strPointer = result.index;
  912. }
  913. } else {
  914. if (numMatches % 2 == 1) { // Open block (Store data)
  915. newStr += str.substring(strPointer, result.index);
  916. strPointer = result.index;
  917. } else { // Close block
  918. newStr += '.'.repeat(result.index - strPointer + blockChars.length);
  919. strPointer = result.index + blockChars.length;
  920. }
  921. }
  922. }
  923.  
  924. if (useInside) {
  925. newStr += '.'.repeat(str.length - strPointer);
  926. } else {
  927. newStr += str.substring(strPointer);
  928. }
  929.  
  930. if (newStr.length != str.length) throw new Error('[E_GetMatchesWithBlock] Failed because the output string didn\'t match input string length');
  931.  
  932. return (newStr.match(new RegExp(exports.escapeRegExp(matchChars), 'g')) || []);
  933. }
  934.  
  935. /*
  936.  
  937. chunkMessage
  938. -Split messages into chunks <= exports.charLimit characters
  939. -Chunks retain format (`, ``, ```, *, **, ***, _, __, ___) and thus account for fitting in format characters
  940. -Message splitting focuses on retaining format over reducing number of chunks:
  941. End ```
  942. Newline + Newline
  943. Newline + Format character(s)
  944. Newline
  945. Space
  946. Any
  947. -Make it so that if last chunk continued string onto next chunk, next chunk cuts at end of that string
  948. */
  949.  
  950. const formatSets = [
  951. ['___', '__'],
  952. ['***', '**', '*'],
  953. ['```', '``', '`'],
  954. ];
  955.  
  956. const splitSets = [ // pivot: -1 = Split Start, 0 = Remove, 1 = Split End
  957. { chars: '```', pivot: 1 }, // Only applies to end ```
  958. { chars: '\n\n', pivot: 0 },
  959. { chars: '\n', pivot: 0 },
  960. { chars: ' ', pivot: 0 },
  961. ];
  962.  
  963. const leaveExtra = formatSets.reduce((a, b) => a.concat(b)).length * 2;
  964.  
  965. function chunkMessage(msg) {
  966. const origChunks = [msg];
  967. let content = msg;
  968. let appendBeginning = [];
  969.  
  970. const baseChunkSize = exports.charLimit - leaveExtra;
  971.  
  972. for (let i = 0; content; ++i, content = origChunks[i]) {
  973. for (let j = 0; j < appendBeginning.length; j++) {
  974. content = appendBeginning[j] + content;
  975. }
  976.  
  977. if (content.length < exports.charLimit) {
  978. origChunks[i] = content;
  979. break;
  980. }
  981.  
  982. let chunk = content.substr(0, baseChunkSize);
  983. let leftOver;
  984.  
  985. appendBeginning = [];
  986.  
  987. for (let j = 0; j < splitSets.length; j++) {
  988. const splitSet = splitSets[j];
  989. const splitChars = splitSet.chars;
  990. const splitType = splitSet.pivot;
  991.  
  992. let pivotStart = chunk.lastIndexOf(splitChars); // exclusive
  993. let pivotEnd = pivotStart; // inclusive
  994.  
  995. if (pivotStart == -1) continue;
  996.  
  997. if (splitType == 1) { // Split End
  998. pivotStart += splitChars.length;
  999. pivotEnd = pivotStart;
  1000. } else if (splitType == 0) { // Remove
  1001. pivotEnd += splitChars.length;
  1002. }
  1003.  
  1004. let chunkTemp = chunk.substring(0, pivotStart);
  1005.  
  1006. if (splitChars == '```') { // Has to be closing a block
  1007. const numSets = (chunkTemp.match(new RegExp(exports.escapeRegExp(splitChars), 'g')) || []).length;
  1008. if (numSets % 2 == 1) {
  1009. if (numSets == 1) continue;
  1010. pivotStart = chunk.substring(0, pivotStart - splitChars.length).lastIndexOf(splitChars);
  1011. if (pivotStart == -1) continue;
  1012. pivotStart += splitChars.length;
  1013. pivotEnd = pivotStart;
  1014. chunkTemp = chunk.substring(0, pivotStart);
  1015. }
  1016. }
  1017.  
  1018. if (chunkTemp.length <= leaveExtra) continue;
  1019.  
  1020. Util.log(`Split on ${splitChars} @ ${pivotStart} @ ${pivotEnd}`);
  1021.  
  1022. chunk = chunkTemp;
  1023. leftOver = content.substr(pivotEnd);
  1024.  
  1025. /* if (i == 1) {
  1026. Util.log(chunkTemp);
  1027. Util.log('---');
  1028. Util.log(leftOver);
  1029. } */
  1030.  
  1031. break;
  1032. }
  1033.  
  1034. if (leftOver == null) {
  1035. Util.log('Split on last');
  1036. leftOver = content.substr(baseChunkSize);
  1037. }
  1038.  
  1039. for (let j = 0; j < formatSets.length; j++) {
  1040. const formatSet = formatSets[j];
  1041.  
  1042. for (let k = 0; k < formatSet.length; k++) {
  1043. const formatChars = formatSet[k];
  1044. const numSets = getMatchesWithBlock(chunk, formatChars, '```', false).length; // Should really only be counting matches not inside code blocks
  1045.  
  1046. if (numSets % 2 == 1) {
  1047. chunk += formatChars;
  1048. appendBeginning.push(formatChars);
  1049. break;
  1050. }
  1051. }
  1052. }
  1053.  
  1054. if (chunk.substr(chunk.length - 3, 3) == '```') appendBeginning.push('​\n');
  1055.  
  1056. origChunks[i] = chunk;
  1057.  
  1058. if (leftOver && leftOver.length > 0) origChunks.push(leftOver);
  1059. }
  1060.  
  1061. return origChunks;
  1062. }
  1063.  
  1064. exports.splitMessages = function (messages) {
  1065. return chunkMessage(messages.join(' '));
  1066. };
  1067.  
  1068. exports.print = function (channel, ...args) {
  1069. const messages = exports.splitMessages(args);
  1070. const promises = [];
  1071. for (let i = 0; i < messages.length; i++) {
  1072. const msg = messages[i];
  1073. // Util.log(`${channel.name}: ${msg.length}`);
  1074. promises.push(channel.send(msg));
  1075. }
  1076. return Promise.all(promises);
  1077. };
  1078.  
  1079. exports.sortPerms = function (permsArr) {
  1080. permsArr.sort((a, b) => exports.permissionsOrder[b] - exports.permissionsOrder[a]);
  1081. };
  1082.  
  1083. exports.getGuildRoles = function (guild) {
  1084. return Array.from(guild.roles.values()).sort((a, b) => b.position - a.position); // From highest to lowest
  1085. };
  1086.  
  1087. exports.getName = function (userResolvable) {
  1088. if (userResolvable == null) return null;
  1089. if (typeof userResolvable === 'string') return userResolvable;
  1090. return Util.isMember(userResolvable) ? userResolvable.user.username : userResolvable.username;
  1091. };
  1092.  
  1093. exports.getMostName = function (userResolvable) {
  1094. if (userResolvable == null) return null;
  1095. if (typeof userResolvable === 'string') return userResolvable;
  1096. const username = exports.getName(userResolvable);
  1097. const discrim = Util.isMember(userResolvable) ? userResolvable.user.discriminator : userResolvable.discriminator;
  1098. return `${username}#${discrim}`;
  1099. };
  1100.  
  1101. exports.getFullName = function (userResolvable, strict) {
  1102. if (userResolvable == null) return strict ? null : 'null'; // TODO: Make strict default at some point
  1103. if (typeof userResolvable === 'string') return userResolvable;
  1104. const mostName = exports.getMostName(userResolvable);
  1105. return `${mostName} (${userResolvable.id})`;
  1106. };
  1107.  
  1108. exports.getDisplayName = function (member) {
  1109. const result = member.displayName || member.username;
  1110. return result;
  1111. };
  1112.  
  1113. exports.getMention = function (userResolvable, full) {
  1114. let out;
  1115.  
  1116. if (userResolvable.user) { // Member
  1117. out = userResolvable.toString();
  1118. } else if (userResolvable.id) { // User
  1119. out = full ? exports.getFullName(userResolvable) : exports.getMostName(userResolvable);
  1120. } else { // Id
  1121. out = `<@${userResolvable}>`;
  1122. }
  1123.  
  1124. return out;
  1125. };
  1126.  
  1127. exports.getAvatar = function (userResolvable, outStr) {
  1128. if (userResolvable != null && exports.isObject(userResolvable)) {
  1129. if (userResolvable.user) userResolvable = userResolvable.user;
  1130. return userResolvable.displayAvatarURL({ format: 'png' });
  1131. }
  1132. return (outStr === true ? 'null' : null);
  1133. };
  1134.  
  1135. exports.getDateString = function (d) {
  1136. if (d == null) d = new Date();
  1137. const result = `${DateFormat(d, 'ddd, mmm dS yyyy @ h:MM TT')} GMT`;
  1138. return result;
  1139. };
  1140.  
  1141. exports.hasRole = (member, role) => member.roles.has(role.id);
  1142.  
  1143. exports.hasRoleName = (member, name) => {
  1144. name = name.toLowerCase();
  1145. const hasRoleVal = member.roles.some(role => role.name.toLowerCase().includes(name));
  1146. return hasRoleVal;
  1147. };
  1148.  
  1149. exports.makeEmbedFooter = function (user, dateParam) {
  1150. const memberName = exports.isObject(user) ? exports.getDisplayName(user) : String(user);
  1151. let date = dateParam;
  1152. if (date == null) date = new Date();
  1153. const dateStr = exports.getDateString(date);
  1154. return { text: `${memberName} | ${dateStr}`, icon_url: exports.getAvatar(user) };
  1155. };
  1156.  
  1157. exports.getSuffix = function (n) {
  1158. const j = n % 10;
  1159. const k = n % 100;
  1160. if (j == 1 && k != 11) {
  1161. return `${n}st`;
  1162. }
  1163. if (j == 2 && k != 12) {
  1164. return `${n}nd`;
  1165. }
  1166. if (j == 3 && k != 13) {
  1167. return `${n}rd`;
  1168. }
  1169. return `${n}th`;
  1170. };
  1171.  
  1172. /*
  1173.  
  1174. If nowString is less than or exactly 512 characters set as field value and return nowFieldNum
  1175. Find last newline under 512 characters
  1176. If none exists then trim, set and return nowFieldNum
  1177. Set everything before newline as value for the field
  1178. Create new field immediately after current field
  1179. Set name as zero width character
  1180. Return function on new field and string after newline
  1181.  
  1182. */
  1183.  
  1184. exports.setFieldValue = function (embFields, nowFieldNum, nowString) {
  1185. const nowField = embFields[nowFieldNum];
  1186. if (nowString.length <= 512) {
  1187. nowField.value = nowString;
  1188. return nowFieldNum;
  1189. }
  1190. let subFirst = nowString.substr(0, 512);
  1191. let subNext;
  1192. const lastNewline = subFirst.lastIndexOf('\n');
  1193. if (lastNewline < 0) {
  1194. const lastSpace = subFirst.lastIndexOf(' ');
  1195. if (lastSpace < 0) {
  1196. subNext = nowString.substring(512);
  1197. } else {
  1198. subFirst = nowString.substring(0, lastSpace);
  1199. subNext = nowString.substring(lastSpace + 1);
  1200. }
  1201. } else {
  1202. subFirst = nowString.substring(0, lastNewline);
  1203. subNext = nowString.substring(lastNewline + 1);
  1204. }
  1205. nowField.value = subFirst;
  1206. const newFieldNum = nowFieldNum + 1;
  1207. embFields.splice(newFieldNum, 0, { name: '​', value: '', inline: nowField.inline });
  1208. return exports.setFieldValue(embFields, newFieldNum, subNext);
  1209. };
  1210.  
  1211. /*
  1212.  
  1213. Max characters:
  1214.  
  1215. Title: 256
  1216. Description: 2048
  1217. Footer: 2048
  1218. Field Name: 256
  1219. Field Value: 512 (maybe 1024?)
  1220.  
  1221. */
  1222.  
  1223. exports.sendEmbed = function (embChannel, embTitle, embDesc, embFooterParam, embImage, embColor, embFieldsParam, isContinued) {
  1224. if (embChannel == null) return;
  1225.  
  1226. let embFooter = embFooterParam;
  1227. let embFields = embFieldsParam;
  1228.  
  1229. let manyFields = false;
  1230. let extraFields;
  1231.  
  1232. if (embFields == null) embFields = [];
  1233.  
  1234. for (let i = embFields.length - 1; i >= 0; i--) {
  1235. if (!embFields[i].name) embFields.splice(i, 1);
  1236. }
  1237.  
  1238. if (embFields.length > 25) {
  1239. manyFields = true;
  1240. extraFields = embFields.splice(25);
  1241. }
  1242.  
  1243. for (let i = 0; i < embFields.length; i++) {
  1244. const nowField = embFields[i];
  1245.  
  1246. if (!has.call(nowField, 'inline')) nowField.inline = true;
  1247.  
  1248. let nowName = nowField.name;
  1249. let nowValue = nowField.value;
  1250.  
  1251. nowName = exports.safeEveryone(String(nowName == null ? 'N/A' : nowName));
  1252. nowValue = exports.safeEveryone(String(nowValue == null ? 'N/A' : nowValue));
  1253.  
  1254. nowField.name = nowName.trim().length < 1 ? 'N/A' : nowName.substr(0, 256);
  1255.  
  1256. if (nowValue.trim().length < 1) {
  1257. nowField.value = 'N/A';
  1258. } else if (nowValue.length > 512) {
  1259. i = exports.setFieldValue(embFields, i, nowValue);
  1260. } else {
  1261. nowField.value = nowValue;
  1262. }
  1263. }
  1264.  
  1265. const embDescStr = String(embDesc);
  1266.  
  1267. let newTitle;
  1268. let newFooter;
  1269. let newDesc = ((embDesc == null || embDescStr.trim().length < 1) ? '​' : embDescStr.substr(0, 2048));
  1270.  
  1271. if (embTitle) newTitle = embTitle.substr(0, 256);
  1272. if (embFooter) {
  1273. if (!exports.isObject(embFooter)) {
  1274. embFooter = { text: embFooter };
  1275. }
  1276. newFooter = exports.cloneObj(embFooter);
  1277. newFooter.text = (newFooter.text).substr(0, 2048);
  1278. }
  1279.  
  1280. if (isContinued) {
  1281. newTitle = null;
  1282. if (newDesc.length < 1 || newDesc === '​') newDesc = null;
  1283. }
  1284.  
  1285. if (manyFields) {
  1286. newFooter = null;
  1287. }
  1288.  
  1289. const embObj = {
  1290. title: newTitle,
  1291. description: newDesc,
  1292. fields: embFields,
  1293. footer: newFooter,
  1294. thumbnail: { url: embImage },
  1295. color: embColor,
  1296. };
  1297.  
  1298. embChannel.send(undefined, { embed: embObj })
  1299. .catch((error) => {
  1300. Util.log(`[E_SendEmbed] ${error} ${embChannel}`);
  1301. Util.log(embObj);
  1302. Util.log(JSON.stringify(embFields));
  1303. });
  1304.  
  1305. if (manyFields) {
  1306. exports.sendEmbed(embChannel, embTitle, embDesc, embFooter, embImage, embColor, extraFields, true);
  1307. }
  1308. };
  1309.  
  1310. exports.sendDescEmbed = function (embChannel, embTitle, embDesc, embFooter, embImage, embColorParam) {
  1311. if (embChannel == null) return;
  1312.  
  1313. let embColor = embColorParam;
  1314.  
  1315. if (embColor == null) embColor = colBlue;
  1316.  
  1317. if (embDesc != null && embDesc.length > 2048) {
  1318. let subFirst = embDesc.substr(0, 2048);
  1319. let subNext;
  1320. const lastNewline = subFirst.lastIndexOf('\n');
  1321. if (lastNewline < 0) {
  1322. const lastSpace = subFirst.lastIndexOf(' ');
  1323. if (lastSpace < 0) {
  1324. subNext = embDesc.substring(2048);
  1325. } else {
  1326. subFirst = embDesc.substring(0, lastSpace);
  1327. subNext = embDesc.substring(lastSpace + 1);
  1328. }
  1329. } else {
  1330. subFirst = embDesc.substring(0, lastNewline);
  1331. subNext = embDesc.substring(lastNewline + 1);
  1332. }
  1333. exports.sendEmbed(embChannel, embTitle, subFirst, null, embImage, embColor, []);
  1334. exports.sendDescEmbed(embChannel, null, subNext, embFooter, embImage, embColor);
  1335. } else {
  1336. exports.sendEmbed(embChannel, embTitle, embDesc, embFooter, embImage, embColor, []);
  1337. }
  1338. };
  1339.  
  1340. exports.sendLog = function (embData, embColor) {
  1341. const embTitle = embData[0];
  1342. const embGuild = embData[1];
  1343. const embAuthor = embData[2];
  1344. const embFields = embData.splice(3);
  1345.  
  1346. for (let i = embFields.length - 1; i >= 0; i--) {
  1347. if (!embFields[i].name) embFields.splice(i, 1);
  1348. }
  1349.  
  1350. const embedTitleLower = embTitle.toLowerCase();
  1351.  
  1352. const logChannel = exports.findChannel('vaebot-log', embGuild);
  1353. if (logChannel) {
  1354. const embFooter = exports.makeEmbedFooter(embAuthor);
  1355. const embAvatar = exports.getAvatar(embAuthor);
  1356.  
  1357. exports.sendEmbed(
  1358. logChannel,
  1359. exports.cloneObj(embTitle),
  1360. null,
  1361. exports.cloneObj(embFooter),
  1362. exports.cloneObj(embAvatar),
  1363. exports.cloneObj(embColor),
  1364. exports.cloneObj(embFields));
  1365. }
  1366.  
  1367. const regex = /(\S*)(?:warn|mute|kick|ban)/i;
  1368. const pre = (regex.exec(embedTitleLower) || [])[1];
  1369.  
  1370. const modChannel = exports.findChannel('mod-logs', embGuild);
  1371. if (modChannel && pre != null && pre != 'un' && !embedTitleLower.includes('revert') && !embedTitleLower.includes('cleared')) {
  1372. const embFooter = exports.makeEmbedFooter(embAuthor);
  1373. const embAvatar = exports.getAvatar(embAuthor);
  1374.  
  1375. exports.sendEmbed(
  1376. modChannel,
  1377. exports.cloneObj(embTitle),
  1378. null,
  1379. exports.cloneObj(embFooter),
  1380. exports.cloneObj(embAvatar),
  1381. exports.cloneObj(embColor),
  1382. exports.cloneObj(embFields));
  1383. }
  1384. };
  1385.  
  1386. exports.getHourStr = function (d) {
  1387. let valStr = (d.getHours()).toString();
  1388. if (valStr.length < 2) valStr = `0${valStr}`;
  1389. return valStr;
  1390. };
  1391.  
  1392. exports.getMinStr = function (d) {
  1393. let valStr = (d.getMinutes()).toString();
  1394. if (valStr.length < 2) valStr = `0${valStr}`;
  1395. return valStr;
  1396. };
  1397.  
  1398. exports.getYearStr = function (d) {
  1399. const valStr = (d.getFullYear()).toString();
  1400. return valStr;
  1401. };
  1402.  
  1403. exports.getMonthStr = function (d) {
  1404. let valStr = (d.getMonth() + 1).toString();
  1405. if (valStr.length < 2) valStr = `0${valStr}`;
  1406. return valStr;
  1407. };
  1408.  
  1409. exports.getDayStr = function (d) {
  1410. let valStr = (d.getDate()).toString();
  1411. if (valStr.length < 2) valStr = `0${valStr}`;
  1412. return valStr;
  1413. };
  1414.  
  1415. /* function searchPartial(array, name, checkPartial) {
  1416. if (checkPartial != false) {
  1417. var firstChar = name.substr(0, 1);
  1418. var endChar = name.substr(name.length-1, 1);
  1419. if (firstChar == "\"" && endChar == "\"") {
  1420. checkPartial = false;
  1421. name = name.substring(1, name.length-1);
  1422. if (name.length < 1) return;
  1423. }
  1424. }
  1425. name = name.toLowerCase()
  1426. var user = array.find(function(item) {
  1427. var user = exports.getName(item);
  1428. if (checkPartial != false ? exports.safe(user.toLowerCase()).includes(name) : exports.safe(user.toLowerCase()) == name) {
  1429. return true;
  1430. }
  1431. return false;
  1432. })
  1433. return user;
  1434. } */
  1435.  
  1436. exports.searchUserPartial = function (container, name) {
  1437. name = name.toLowerCase();
  1438. return container.find((user) => {
  1439. const username = exports.getName(user);
  1440. if (user.id === name || exports.safe(username.toLowerCase()).includes(name)) {
  1441. return true;
  1442. }
  1443. return false;
  1444. });
  1445. };
  1446.  
  1447. exports.round = function (num, inc) {
  1448. return inc == 0 ? num : Math.floor((num / inc) + 0.5) * inc;
  1449. };
  1450.  
  1451. exports.write = function (content, name) {
  1452. FileSys.writeFile(name, content);
  1453. };
  1454.  
  1455. exports.remove = function (name) {
  1456. FileSys.unlink(name);
  1457. };
  1458.  
  1459. exports.resolveUserMention = function (guild, id) {
  1460. let resolvedUser;
  1461.  
  1462. if (id == null) {
  1463. id = guild;
  1464. resolvedUser = client.users.get(id);
  1465. } else {
  1466. resolvedUser = exports.getMemberById(id, guild);
  1467. }
  1468.  
  1469. if (resolvedUser) return resolvedUser.toString();
  1470.  
  1471. return `<@${id}>`;
  1472. };
  1473.  
  1474. exports.getNumMutes = async function (id, guild) {
  1475. const pastMutes = await Data.getRecords(guild, 'mutes', { user_id: id });
  1476. return pastMutes.length;
  1477. };
  1478.  
  1479. exports.historyToStringOld = function (num) {
  1480. let timeHours = exports.round(num / 3600000, 0.1);
  1481. timeHours = (timeHours >= 1 || timeHours == 0) ? timeHours.toFixed(0) : timeHours.toFixed(1);
  1482. Util.log(`[RANDOM] timeHours: ${timeHours}`);
  1483. return timeHours + (timeHours == 1 ? ' hour' : ' hours');
  1484. };
  1485.  
  1486. exports.historyToStringOld2 = function (num) {
  1487. let timeHours = num / 3600000;
  1488. Util.log(`[RANDOM] timeHours: ${timeHours}`);
  1489. timeHours += (timeHours == 1 ? ' hour' : ' hours');
  1490. return timeHours;
  1491. };
  1492.  
  1493. exports.historyToString = function (num) {
  1494. const timeStr = exports.formatTime(num);
  1495. return timeStr;
  1496. };
  1497.  
  1498. exports.matchWholeNumber = function (str) {
  1499. let result = str.match(/^\d*(?:\.\d+)?$/);
  1500. result = result ? result[0] : undefined;
  1501. return result;
  1502. };
  1503.  
  1504. exports.getSafeId = function (id) {
  1505. id = id.match(/\d+/);
  1506.  
  1507. if (id == null) return undefined;
  1508.  
  1509. return id[0];
  1510. };
  1511.  
  1512. exports.getMemberById = function (id, guild) {
  1513. if (id == null || guild == null) return null;
  1514.  
  1515. if (id.substr(0, 1) === '<' && id.substr(id.length - 1, 1) === '>') id = exports.getSafeId(id);
  1516.  
  1517. if (id == null || id.length < 1) return null;
  1518.  
  1519. return guild.members.get(id);
  1520. };
  1521.  
  1522. exports.isId = function (str) {
  1523. let id = str.match(/^\d+$/);
  1524.  
  1525. if (id == null) {
  1526. id = str.match(/^<.?(\d+)>$/);
  1527. if (id == null) return undefined;
  1528. id = id[1];
  1529. } else {
  1530. id = id[0];
  1531. }
  1532.  
  1533. if (id.length < 17 || id.length > 19) return undefined;
  1534.  
  1535. return id;
  1536. };
  1537.  
  1538. exports.getMatchStrength = function (fullStr, subStr) { // [v2.0]
  1539. let value = 0;
  1540.  
  1541. const fullStrLower = fullStr.toLowerCase();
  1542. const subStrLower = subStr.toLowerCase();
  1543.  
  1544. const nameMatch = fullStrLower.indexOf(subStrLower);
  1545.  
  1546. if (nameMatch >= 0) {
  1547. const filled = Math.min(subStr.length / fullStr.length, 0.999);
  1548. value += 2 ** (2 + filled);
  1549.  
  1550. const maxCaps = Math.min(subStr.length, fullStr.length);
  1551. let numCaps = 0;
  1552. for (let j = 0; j < maxCaps; j++) {
  1553. if (subStr[j] === fullStr[nameMatch + j]) numCaps++;
  1554. }
  1555. const caps = Math.min(numCaps / maxCaps, 0.999);
  1556. value += 2 ** (1 + caps);
  1557.  
  1558. const totalPosition = fullStr.length - subStr.length;
  1559. const perc = 1 - (totalPosition * nameMatch == 0 ? 0.001 : nameMatch / totalPosition);
  1560. value += 2 ** perc;
  1561. }
  1562.  
  1563. return value;
  1564. };
  1565.  
  1566. exports.getDiscriminatorFromName = function (name) {
  1567. const discrimPattern = /#(\d\d\d\d)$/gm;
  1568. let discrim = discrimPattern.exec(name);
  1569. discrim = discrim ? discrim[1] : null;
  1570. return discrim;
  1571. };
  1572.  
  1573. exports.isNumeric = function (str) {
  1574. return !isNaN(parseFloat(str)) && isFinite(str);
  1575. };
  1576.  
  1577. exports.entirelyNumbers = function (str) {
  1578. return /^\d+$/.test(str);
  1579. };
  1580.  
  1581. exports.getBestMatch = function (container, key, name) { // [v3.0] Visible name match, real name match, length match, caps match, position match
  1582. if (container == null) return undefined;
  1583.  
  1584. let removeUnicode = false;
  1585.  
  1586. if (key === 'username') {
  1587. removeUnicode = true;
  1588. const nameDiscrim = exports.getDiscriminatorFromName(name);
  1589. if (nameDiscrim) {
  1590. const namePre = name.substr(0, name.length - 5);
  1591. const user = container.find(m => m.username === namePre && m.discriminator === nameDiscrim);
  1592. if (user) return user;
  1593. }
  1594. }
  1595.  
  1596. const origName = name.trim();
  1597.  
  1598. if (removeUnicode) {
  1599. name = name.replace(/[^\x00-\x7F]/g, '').trim();
  1600. if (name.length == 0) {
  1601. name = origName;
  1602. removeUnicode = false;
  1603. }
  1604. }
  1605.  
  1606. const str2Lower = name.toLowerCase();
  1607. let strongest = null;
  1608.  
  1609. container.forEach((obj) => {
  1610. let realName = obj[key];
  1611. if (removeUnicode) realName = realName.replace(/[^\x00-\x7F]/g, '');
  1612. realName = realName.trim();
  1613. const nameMatch = realName.toLowerCase().indexOf(str2Lower);
  1614.  
  1615. const strength = { 'obj': obj };
  1616. let layer = 0;
  1617.  
  1618. if (nameMatch >= 0) {
  1619. strength[layer++] = 1;
  1620.  
  1621. // Util.log("(" + i + ") " + realName + ": " + value);
  1622. const filled = Math.min(name.length / realName.length, 0.999);
  1623. // Util.log("filled: " + filled);
  1624. strength[layer++] = filled;
  1625.  
  1626. const maxCaps = Math.min(name.length, realName.length);
  1627. let numCaps = 0;
  1628. for (let j = 0; j < maxCaps; j++) {
  1629. if (name[j] === realName[nameMatch + j]) numCaps++;
  1630. }
  1631. const caps = Math.min(numCaps / maxCaps, 0.999);
  1632. // const capsExp = (filledExp * 0.5 - 1 + caps);
  1633. // Util.log("caps: " + caps + " (" + numCaps + "/" + maxCaps + ")");
  1634. strength[layer++] = caps;
  1635.  
  1636. const totalPosition = realName.length - name.length;
  1637. const perc = 1 - (totalPosition * nameMatch == 0 ? 0.001 : nameMatch / totalPosition);
  1638. // const percExp = (capsExp - 2 + perc);
  1639. // Util.log("pos: " + perc + " (" + nameMatch + "/" + totalPosition + ")");
  1640. strength[layer++] = perc;
  1641.  
  1642. if (strongest == null) {
  1643. strongest = strength;
  1644. } else {
  1645. for (let i = 0; i < layer; i++) {
  1646. if (strength[i] > strongest[i]) {
  1647. strongest = strength;
  1648. break;
  1649. } else if (strength[i] < strongest[i]) {
  1650. break;
  1651. }
  1652. }
  1653. }
  1654. }
  1655. });
  1656.  
  1657. return strongest != null ? strongest.obj : undefined;
  1658. };
  1659.  
  1660. exports.getMemberByName = function (name, guild) { // [v3.0] Visible name match, real name match, length match, caps match, position match
  1661. if (guild == null) return undefined;
  1662.  
  1663. const nameDiscrim = exports.getDiscriminatorFromName(name);
  1664. if (nameDiscrim) {
  1665. const namePre = name.substr(0, name.length - 5);
  1666. const member = guild.members.find(m => m.user.username === namePre && m.user.discriminator === nameDiscrim);
  1667. if (member) return member;
  1668. }
  1669.  
  1670. let removeUnicode = true;
  1671. const origName = name.trim();
  1672.  
  1673. name = name.replace(/[^\x00-\x7F]/g, '').trim();
  1674.  
  1675. if (name.length == 0) {
  1676. name = origName;
  1677. removeUnicode = false;
  1678. }
  1679.  
  1680. const str2Lower = name.toLowerCase();
  1681. const members = guild.members;
  1682. let strongest = null;
  1683.  
  1684. if (str2Lower == 'vaeb') {
  1685. const selfMember = members.get(vaebId);
  1686. if (selfMember) return selfMember;
  1687. }
  1688.  
  1689. members.forEach((member) => {
  1690. let realName = member.nickname != null ? member.nickname : exports.getName(member);
  1691. if (removeUnicode) realName = realName.replace(/[^\x00-\x7F]/g, '');
  1692. realName = realName.trim();
  1693. let realstr2Lower = realName.toLowerCase();
  1694. let nameMatch = realstr2Lower.indexOf(str2Lower);
  1695.  
  1696. const strength = { 'member': member };
  1697. let layer = 0;
  1698.  
  1699. if (nameMatch >= 0) {
  1700. strength[layer++] = 2;
  1701. } else {
  1702. realName = exports.getName(member);
  1703. if (removeUnicode) realName = realName.replace(/[^\x00-\x7F]/g, '');
  1704. realName = realName.trim();
  1705. realstr2Lower = realName.toLowerCase();
  1706. nameMatch = realstr2Lower.indexOf(str2Lower);
  1707. if (nameMatch >= 0) {
  1708. strength[layer++] = 1;
  1709. }
  1710. }
  1711.  
  1712. if (nameMatch >= 0) {
  1713. // Util.log("(" + i + ") " + realName + ": " + value);
  1714. const filled = Math.min(name.length / realName.length, 0.999);
  1715. // Util.log("filled: " + filled);
  1716. strength[layer++] = filled;
  1717.  
  1718. const maxCaps = Math.min(name.length, realName.length);
  1719. let numCaps = 0;
  1720. for (let j = 0; j < maxCaps; j++) {
  1721. if (name[j] === realName[nameMatch + j]) numCaps++;
  1722. }
  1723. const caps = Math.min(numCaps / maxCaps, 0.999);
  1724. // const capsExp = (filledExp * 0.5 - 1 + caps);
  1725. // Util.log("caps: " + caps + " (" + numCaps + "/" + maxCaps + ")");
  1726. strength[layer++] = caps;
  1727.  
  1728. const totalPosition = realName.length - name.length;
  1729. const perc = 1 - (totalPosition * nameMatch == 0 ? 0.001 : nameMatch / totalPosition);
  1730. // const percExp = (capsExp - 2 + perc);
  1731. // Util.log("pos: " + perc + " (" + nameMatch + "/" + totalPosition + ")");
  1732. strength[layer++] = perc;
  1733.  
  1734. if (strongest == null) {
  1735. strongest = strength;
  1736. } else {
  1737. for (let i = 0; i < layer; i++) {
  1738. if (strength[i] > strongest[i]) {
  1739. strongest = strength;
  1740. break;
  1741. } else if (strength[i] < strongest[i]) {
  1742. break;
  1743. }
  1744. }
  1745. }
  1746. }
  1747. });
  1748.  
  1749. return strongest != null ? strongest.member : undefined;
  1750. };
  1751.  
  1752. exports.getMemberByNameOld = function (name, guild) { // [v2.0] Visible name match, real name match, length match, caps match, position match //
  1753. if (guild == null) return undefined;
  1754.  
  1755. const nameDiscrim = exports.getDiscriminatorFromName(name);
  1756. if (nameDiscrim) {
  1757. const namePre = name.substr(0, name.length - 5);
  1758. const member = guild.members.find(m => m.user.username === namePre && m.user.discriminator === nameDiscrim);
  1759. if (member) return member;
  1760. }
  1761.  
  1762. let removeUnicode = true;
  1763. const origName = name.trim();
  1764.  
  1765. name = name.replace(/[^\x00-\x7F]/g, '');
  1766. name = name.trim();
  1767.  
  1768. if (name.length == 0) {
  1769. name = origName;
  1770. removeUnicode = false;
  1771. }
  1772.  
  1773. const str2Lower = name.toLowerCase();
  1774.  
  1775. const members = guild.members;
  1776. const matchStrength = [];
  1777. let strongest = [0, undefined];
  1778.  
  1779. members.forEach((member) => {
  1780. let value = 0;
  1781.  
  1782. let realName = member.nickname != null ? member.nickname : exports.getName(member);
  1783. if (removeUnicode) realName = realName.replace(/[^\x00-\x7F]/g, '');
  1784. realName = realName.trim();
  1785. let realstr2Lower = realName.toLowerCase();
  1786. let nameMatch = realstr2Lower.indexOf(str2Lower);
  1787.  
  1788. if (nameMatch >= 0) {
  1789. value += 2 ** 5;
  1790. } else {
  1791. realName = exports.getName(member);
  1792. if (removeUnicode) realName = realName.replace(/[^\x00-\x7F]/g, '');
  1793. realName = realName.trim();
  1794. realstr2Lower = realName.toLowerCase();
  1795. nameMatch = realstr2Lower.indexOf(str2Lower);
  1796. if (nameMatch >= 0) {
  1797. value += 2 ** 4;
  1798. }
  1799. }
  1800.  
  1801. if (nameMatch >= 0) {
  1802. // Util.log("(" + i + ") " + realName + ": " + value);
  1803. const filled = Math.min(name.length / realName.length, 0.999);
  1804. const filledExp = (2 + filled);
  1805. // Util.log("filled: " + filled);
  1806. value += 2 ** filledExp;
  1807.  
  1808. const maxCaps = Math.min(name.length, realName.length);
  1809. let numCaps = 0;
  1810. for (let j = 0; j < maxCaps; j++) {
  1811. if (name[j] === realName[nameMatch + j]) numCaps++;
  1812. }
  1813. const caps = Math.min(numCaps / maxCaps, 0.999);
  1814. // const capsExp = (filledExp * 0.5 - 1 + caps);
  1815. const capsExp = (1 + caps);
  1816. // Util.log("caps: " + caps + " (" + numCaps + "/" + maxCaps + ")");
  1817. value += 2 ** capsExp;
  1818.  
  1819. const totalPosition = realName.length - name.length;
  1820. const perc = 1 - (totalPosition * nameMatch == 0 ? 0.001 : nameMatch / totalPosition);
  1821. // const percExp = (capsExp - 2 + perc);
  1822. const percExp = (0 + perc);
  1823. // Util.log("pos: " + perc + " (" + nameMatch + "/" + totalPosition + ")");
  1824. value += 2 ** percExp;
  1825.  
  1826. // Util.log(value);
  1827. matchStrength.push([value, member]);
  1828. }
  1829. });
  1830.  
  1831. for (let i = 0; i < matchStrength.length; i++) {
  1832. const strength = matchStrength[i];
  1833. if (strength[0] > strongest[0]) strongest = strength;
  1834. }
  1835.  
  1836. return strongest[1];
  1837. };
  1838.  
  1839. function getDataFromStringInner(str, funcs, returnExtra) {
  1840. const mix = str.split(' ');
  1841. const baseStart = mix.length - 1;
  1842. let start = baseStart;
  1843. let end = 0;
  1844. let pos = start;
  1845. let index = 0;
  1846. let combine = [];
  1847. const results = [];
  1848. while (start >= 0) {
  1849. const remainingFuncs = funcs.length - index - 1;
  1850. const remainingTerms = baseStart - (start);
  1851. if (remainingTerms < remainingFuncs) {
  1852. start--;
  1853. pos = start;
  1854. combine = [];
  1855. continue;
  1856. }
  1857. const chunk = mix[pos];
  1858. if (chunk != null) combine.unshift(chunk);
  1859. if (pos <= end) {
  1860. const result = funcs[index](combine.join(' '), results);
  1861. if (result != null) {
  1862. /* if (index == 1) {
  1863. Util.log("[Z] " + combine.join(" "));
  1864. Util.log("[Z] " + remainingFuncs);
  1865. Util.log("[Z] " + remainingTerms);
  1866. Util.log("[Z] " + pos);
  1867. Util.log("[Z] " + start);
  1868. Util.log("[Z] " + end);
  1869. Util.log("[Z] " + result);
  1870. } */
  1871. results.push(result);
  1872. index++;
  1873. if (index >= funcs.length) {
  1874. if (returnExtra) {
  1875. combine = [];
  1876. for (let i = start + 1; i < mix.length; i++) {
  1877. const extra = mix[i];
  1878. if (extra != null) combine.push(extra);
  1879. }
  1880. let leftOver = '';
  1881. if (combine.length > 0) leftOver = combine.join(' ');
  1882. results.push(leftOver);
  1883. }
  1884. return results;
  1885. }
  1886. end = start + 1;
  1887. if (end > baseStart) return undefined;
  1888. start = baseStart;
  1889. } else {
  1890. start--;
  1891. }
  1892. pos = start;
  1893. combine = [];
  1894. } else {
  1895. pos--;
  1896. }
  1897. }
  1898.  
  1899. return undefined;
  1900. }
  1901.  
  1902. /*
  1903.  
  1904. If optional parameters:
  1905. -Fixed order
  1906. -Only one function per optional parameter (so no need for putting each function in its own array)
  1907. -Some optional parameters might only be an option if a previous optional parameter exists
  1908. -Some optional parameters might not have a space before them
  1909. -Don't know which parameters are being used
  1910.  
  1911. ;cmd optionalParam1 name optionalParam2DependsOnOP1 optionalParam2 time
  1912.  
  1913. const data = Util.getDataFromString(args,
  1914. [
  1915. {
  1916. func: function (str) {
  1917. return Util.getMemberByMixed(str, guild) || Util.isId(str);
  1918. },
  1919. },
  1920. {
  1921. func: function (str) {
  1922. const timeHours = Util.matchWholeNumber(str);
  1923. return timeHours;
  1924. },
  1925. optional: true,
  1926. },
  1927. {
  1928. func: function (str) {
  1929. let mult;
  1930. str = str.toLowerCase();
  1931. if (str.substr(str.length - 1, 1) == 's' && str.length > 2) str = str.substr(0, str.length - 1);
  1932. if (str == 'millisecond' || str == 'ms') mult = 1 / 60 / 60 / 1000;
  1933. if (str == 'second' || str == 's' || str == 'sec') mult = 1 / 60 / 60;
  1934. if (str == 'minute' || str == 'm' || str == 'min') mult = 1 / 60;
  1935. if (str == 'hour' || str == 'h') mult = 1;
  1936. if (str == 'day' || str == 'd') mult = 24;
  1937. if (str == 'week' || str == 'w') mult = 24 * 7;
  1938. if (str == 'month' || str == 'mo') mult = 24 * 30.42;
  1939. if (str == 'year' || str == 'y') mult = 24 * 365.2422;
  1940. return mult;
  1941. },
  1942. optional: true,
  1943. requires: 1,
  1944. prefix: / ?/,
  1945. },
  1946. ]
  1947. , true);
  1948.  
  1949. - Get all possible variations that match the prefixes
  1950.  
  1951. */
  1952.  
  1953. exports.getDataFromString = function (str, funcSets, returnExtra) {
  1954. if (typeof funcSets[0] == 'function') return getDataFromStringInner(str, funcSets, returnExtra);
  1955.  
  1956. const mainData = getDataFromStringInner(str, funcSets[0], returnExtra);
  1957.  
  1958. if (!mainData) return mainData;
  1959.  
  1960. let lastExtra = mainData[funcSets[0].length];
  1961. mainData.splice(funcSets[0].length);
  1962.  
  1963. for (let i = 1; i < funcSets.length; i++) {
  1964. if (!lastExtra || lastExtra.length == 0) break;
  1965. const nowData = getDataFromStringInner(lastExtra, funcSets[i], returnExtra);
  1966. if (nowData) {
  1967. for (let j = 0; j < funcSets[i].length; j++) mainData.push(nowData[j]);
  1968. lastExtra = nowData[funcSets[i].length];
  1969. } else {
  1970. for (let j = 0; j < funcSets[i].length; j++) mainData.push(null);
  1971. }
  1972. }
  1973.  
  1974. mainData.push(lastExtra);
  1975.  
  1976. return mainData;
  1977. };
  1978.  
  1979. function matchSet(str, funcSets, setIndex, data) {
  1980. if (setIndex >= funcSets.length) {
  1981. return true;
  1982. }
  1983. data.fail = Math.max(data.fail, setIndex);
  1984. Util.log(`Loop ${setIndex}`);
  1985. const set = funcSets[setIndex++];
  1986. if (set.requires && data[set.requires] === undefined) {
  1987. Util.log('Missing requires');
  1988. if (!set.optional) return false;
  1989. data.push(undefined);
  1990. if (matchSet(str, funcSets, setIndex, data)) return true;
  1991. data.pop();
  1992. } else if (str.length === 0) {
  1993. for (let i = setIndex - 1, s; s = funcSets[i]; i++) {
  1994. if (!s.optional) return false;
  1995. }
  1996. return true;
  1997. }
  1998. const pMatch = str.match(set.prefix || (setIndex === 1 ? /\s*/ : /\s+/));
  1999. Util.log('\t', pMatch, setIndex, set.prefix || (setIndex === 1 ? /\s*/ : /\s+/));
  2000. if (!pMatch) return false;
  2001. if (pMatch.index !== 0) return false;
  2002. str = str.substr(pMatch[0].length);
  2003. for (let i = str.length; i >= 0; i--) {
  2004. const part = str.substr(0, i);
  2005. // Util.log(`\tchecking part ${part}`);
  2006. let good = true;
  2007. if (set.match) {
  2008. const mMatch = part.match(set.match);
  2009. good = mMatch[0] == part;
  2010. }
  2011. const res = good && set.func(part);
  2012. if (!res || !good) continue;
  2013. // Util.log("Got", res, "for", data.length, "with length", i);
  2014. data.push(res);
  2015. const left = str.substr(i);
  2016. if (matchSet(left, funcSets, setIndex, data)) {
  2017. // Util.log("Reached the end, yeuy!");
  2018. return true;
  2019. }
  2020. data.pop();
  2021. if (set.longest) return false;
  2022. }
  2023. if (!set.optional) return false;
  2024. return matchSet(str, funcSets, setIndex, data);
  2025. }
  2026.  
  2027. exports.getDataFromString2 = function (str, funcSets, returnExtra) {
  2028. const data = [];
  2029. data.fail = 0;
  2030. const done = [];
  2031. if (returnExtra) {
  2032. funcSets.push({
  2033. func(extra) {
  2034. return extra;
  2035. },
  2036. optional: true,
  2037. });
  2038. }
  2039. const success = matchSet(str, funcSets, 0, data, done);
  2040. data.success = success;
  2041. if (success) {
  2042. for (let i = data.length; i < funcSets.length; i++) {
  2043. data.push(undefined);
  2044. }
  2045. delete data.fail;
  2046. }
  2047. return data;
  2048. };
  2049.  
  2050. exports.clamp = function (num, minParam, maxParam) {
  2051. let min = minParam;
  2052. let max = maxParam;
  2053. if (min == null) min = num;
  2054. if (max == null) max = num;
  2055. return Math.min(Math.max(num, min), max);
  2056. };
  2057.  
  2058. exports.noBlock = function (str) {
  2059. return ` \`\`\`\n${str}\n\`\`\`\n `;
  2060. };
  2061.  
  2062. exports.toBoolean = function (str) {
  2063. const result = (typeof (str) === 'boolean' ? str : (str === 'true' || (str === 'false' ? false : undefined)));
  2064. return result;
  2065. };
  2066.  
  2067. exports.getNum = function (str, min, max) {
  2068. const num = Number(str);
  2069. if (isNaN(num)) return undefined;
  2070. return exports.clamp(num, min, max);
  2071. };
  2072.  
  2073. exports.getInt = function (str, min, max) {
  2074. const num = parseInt(str, 10); // Number() is better generally
  2075. if (isNaN(num)) return undefined;
  2076. return exports.clamp(num, min, max);
  2077. };
  2078.  
  2079. exports.isTextChannel = channel => channel.type === 'text';
  2080.  
  2081. exports.isVoiceChannel = channel => channel.type === 'voice';
  2082.  
  2083. exports.getTextChannels = guild => guild.channels.filter(exports.isTextChannel);
  2084.  
  2085. exports.getVoiceChannels = guild => guild.channels.filter(exports.isVoiceChannel);
  2086.  
  2087. exports.findChannel = function (nameParam, guild) {
  2088. if (guild == null) return undefined;
  2089.  
  2090. let name = nameParam;
  2091.  
  2092. name = name.toLowerCase();
  2093. const channels = exports.getTextChannels(guild);
  2094. return channels.find(nowChannel => nowChannel.id === name || nowChannel.name.toLowerCase() === name);
  2095. };
  2096.  
  2097. exports.findVoiceChannel = function (nameParam, guild) {
  2098. if (guild == null) return undefined;
  2099.  
  2100. let name = nameParam;
  2101.  
  2102. name = name.toLowerCase();
  2103. const channels = exports.getVoiceChannels(guild);
  2104. return channels.find(nowChannel => nowChannel.id === name || nowChannel.name.toLowerCase() === name);
  2105. };
  2106.  
  2107. exports.isAdmin = function (member) {
  2108. const highestRole = member.highestRole;
  2109. const guildRolesFromTop = exports.getGuildRoles(member.guild);
  2110.  
  2111. for (let i = 0; i < guildRolesFromTop.length; i++) {
  2112. const role = guildRolesFromTop[i];
  2113. if (/\bmod/g.test(role.name.toLowerCase())) {
  2114. return false;
  2115. } else if (role.id == highestRole.id) {
  2116. return true;
  2117. }
  2118. }
  2119.  
  2120. return false;
  2121. };
  2122.  
  2123. exports.getRole = function (name, obj) {
  2124. if (obj == null) return undefined;
  2125.  
  2126. name = name.toLowerCase();
  2127.  
  2128. const nameId = exports.getSafeId(name);
  2129. const roles = obj.roles;
  2130. if (roles.has(nameId)) return roles.get(nameId);
  2131.  
  2132. const returnRole = exports.getBestMatch(roles, 'name', name);
  2133.  
  2134. return returnRole;
  2135. };
  2136.  
  2137. exports.getHighestRole = member => member.highestRole;
  2138.  
  2139. exports.getPosition = function (speaker) {
  2140. if (speaker == null || !exports.isObject(speaker) || speaker.guild == null) return undefined;
  2141.  
  2142. if (speaker.id === speaker.guild.ownerID) return 999999999;
  2143.  
  2144. return speaker.highestRole.position;
  2145. };
  2146.  
  2147. exports.getUserById = id => client.users.get(id);
  2148.  
  2149. exports.getUserByName = name => exports.getBestMatch(client.users, 'username', name);
  2150.  
  2151. exports.getUserByMixed = function (name) {
  2152. let user = exports.getUserById(name);
  2153. if (user == null) user = exports.getUserByName(name);
  2154. return user;
  2155. };
  2156.  
  2157. exports.getMemberByMixed = function (name, guild) {
  2158. if (guild == null) return undefined;
  2159. let targetMember = exports.getMemberById(name, guild);
  2160. if (targetMember == null) targetMember = exports.getMemberByName(name, guild);
  2161. return targetMember;
  2162. };
  2163.  
  2164. exports.getMemberOrRoleByMixed = function (name, guild) {
  2165. if (guild == null) return undefined;
  2166. let targetObj = exports.getRole(name, guild);
  2167. if (targetObj == null) targetObj = exports.getMemberById(name, guild);
  2168. if (targetObj == null) targetObj = exports.getMemberByName(name, guild);
  2169. return targetObj;
  2170. };
  2171.  
  2172. exports.getEitherByMixed = function (name, guild) {
  2173. let user = exports.getMemberByMixed(name, guild);
  2174. if (user == null) user = exports.getUserByMixed(name);
  2175. return user;
  2176. };
  2177.  
  2178. exports.permEnabled = function (iPerms, permName) {
  2179. const allowGeneral = iPerms.General;
  2180. const allowText = iPerms.Text;
  2181. const allowVoice = iPerms.Voice;
  2182.  
  2183. if (has.call(allowGeneral, permName)) return allowGeneral[permName];
  2184. if (has.call(allowText, permName)) return allowText[permName];
  2185. if (has.call(allowVoice, permName)) return allowVoice[permName];
  2186.  
  2187. return undefined;
  2188. };
  2189.  
  2190. exports.getRolePermissions = function (role, channel) {
  2191. const outPerms = [];
  2192.  
  2193. if (!channel) {
  2194. for (let i = 0; i < exports.rolePermissions.length; i++) {
  2195. const permName = exports.rolePermissions[i];
  2196. if (role.hasPermission(permName)) {
  2197. outPerms.push(permName);
  2198. }
  2199. }
  2200. }
  2201.  
  2202. return outPerms;
  2203. };
  2204.  
  2205. exports.getPermRating = function (guild, userOrRole) {
  2206. if (userOrRole.hasPermission == null) return 0;
  2207.  
  2208. const tempPermRating = exports.cloneObj(exports.permRating);
  2209.  
  2210. let total = 0;
  2211. let foundTop = false;
  2212.  
  2213. for (let i = 0; i < tempPermRating.length; i++) {
  2214. const permData = tempPermRating[i];
  2215. if (userOrRole.hasPermission(permData[0], false)) {
  2216. if (!foundTop) {
  2217. foundTop = true;
  2218.  
  2219. let lastVal = null;
  2220. let pointer0 = i + 1;
  2221. let pointer1 = i + 1;
  2222. let newVal = 5;
  2223.  
  2224. // Util.log("found", permData[0]);
  2225.  
  2226. for (let i2 = i + 1; i2 < tempPermRating.length; i2++) {
  2227. const nowVal = tempPermRating[i2][1];
  2228. if (lastVal == null) lastVal = nowVal;
  2229. if (nowVal !== lastVal) {
  2230. const numPoints = (pointer1 - pointer0) + 1;
  2231. newVal /= numPoints;
  2232. for (let n = pointer0; n <= pointer1; n++) {
  2233. tempPermRating[n][1] = newVal;
  2234. }
  2235. newVal /= 2;
  2236. pointer0 = i2;
  2237. }
  2238. pointer1 = i2;
  2239. lastVal = nowVal;
  2240. }
  2241.  
  2242. const numPoints = (pointer1 - pointer0) + 1;
  2243. newVal /= numPoints;
  2244. for (let n = pointer0; n <= pointer1; n++) {
  2245. tempPermRating[n][1] = newVal;
  2246. }
  2247.  
  2248. // Util.log(tempPermRating);
  2249. }
  2250. total += permData[1];
  2251. }
  2252. }
  2253.  
  2254. total = Math.min(total, 100);
  2255.  
  2256. return total;
  2257. };
  2258.  
  2259. exports.getMemberPowers = function (guild) {
  2260. const sorted = [];
  2261. const members = guild.members;
  2262. for (let i = 0; i < members.size; i++) {
  2263. const member = members[i];
  2264. const power = exports.getPermRating(guild, member);
  2265. let index = 0;
  2266. for (index = 0; index < sorted.length; index++) {
  2267. if (power >= sorted[index][1]) break;
  2268. }
  2269. sorted.splice(index, 0, [member, power]);
  2270. }
  2271. return sorted;
  2272. };
  2273.  
  2274. exports.strToPerm = function (strParam) {
  2275. let str = strParam;
  2276.  
  2277. str = exports.replaceAll(str.toUpperCase(), ' ', '_');
  2278.  
  2279. let matchPerm = null;
  2280. let matchTop = 0;
  2281.  
  2282. for (const [permName] of Object.entries(exports.permissionsOrder)) {
  2283. const matchScore = exports.getMatchStrength(permName, str);
  2284.  
  2285. if (matchScore > matchTop) {
  2286. matchTop = matchScore;
  2287. matchPerm = permName;
  2288. }
  2289. }
  2290.  
  2291. return matchPerm;
  2292. };
  2293.  
  2294. exports.setChannelPerms = function (channel, userOrRole, newPerms) {
  2295. channel.overwritePermissions(userOrRole, newPerms)
  2296. .catch(error => Util.log(`[E_SetChannelPerms] ${error}`));
  2297. };
  2298.  
  2299. // fetch more messages just like Discord client does
  2300. exports.fetchMessagesEx = function (channel, left, store, lastParam) {
  2301. // message cache is sorted on insertion
  2302. // channel.messages[0] will get oldest message
  2303. let last = lastParam;
  2304.  
  2305. if (last) last = last.id;
  2306. return channel.fetchMessages({ limit: Math.min(left, 100), before: last })
  2307. .then(messages => exports.onFetch(messages, channel, left, store));
  2308. };
  2309.  
  2310. function mirrorProperties(member) {
  2311. const memberProto = Object.getPrototypeOf(member);
  2312. const userProto = Object.getPrototypeOf(member.user);
  2313. for (const key in member.user) {
  2314. if (!Object.getOwnPropertyDescriptor(memberProto, key)) {
  2315. Object.defineProperty(memberProto, key, {
  2316. get() {
  2317. return this.user[key];
  2318. },
  2319. set(val) {
  2320. this.user[key] = val;
  2321. },
  2322. });
  2323. }
  2324. }
  2325. const descriptors = Object.getOwnPropertyDescriptors(userProto);
  2326. for (const key in descriptors) {
  2327. if (!Object.getOwnPropertyDescriptor(memberProto, key)) {
  2328. Object.defineProperty(memberProto, key, {
  2329. get() {
  2330. return this.user[key];
  2331. },
  2332. set(val) {
  2333. this.user[key] = val;
  2334. },
  2335. });
  2336. }
  2337. }
  2338. }
  2339.  
  2340. exports.mergeUser = function (member) {
  2341. // Util.log('Adding new proxy:');
  2342. // Util.log(`Adding proxy to ${String(member)}`);
  2343.  
  2344. /* const oldPrototype = Object.getPrototypeOf(member);
  2345.  
  2346. if (Reflect.has(oldPrototype, 'proxyId')) return false;
  2347.  
  2348. const nowProxyId = proxyId++;
  2349.  
  2350. const userProxy = new Proxy({ proxyId: nowProxyId }, {
  2351. get(storage, prop) {
  2352. Util.log(`Getting ${prop} from ${nowProxyId}`);
  2353. if (Reflect.has(member, prop)) return Reflect.get(member, prop);
  2354. else if (Reflect.has(oldPrototype, prop)) return Reflect.get(oldPrototype, prop, member);
  2355. else if (Reflect.has(member.user, prop)) return Reflect.get(member.user, prop);
  2356. return storage[prop];
  2357. },
  2358. set(storage, prop, val) {
  2359. Util.logc('Setter', 'Setting', prop, 'to', val);
  2360. Reflect.set(member, prop, val); // Could 1st arg be oldPrototype and 4th be member?
  2361. return val;
  2362. },
  2363. getPrototypeOf() {
  2364. return Reflect.getPrototypeOf(member);
  2365. },
  2366. });
  2367.  
  2368. Object.setPrototypeOf(member, userProxy); */
  2369.  
  2370. mirrorProperties(member);
  2371.  
  2372. return true;
  2373. };
  2374.  
  2375. exports.resolveMention = function (userResolvable) {
  2376. if (userResolvable == null) return undefined;
  2377. if (typeof user === 'string') return `<@${userResolvable}>`;
  2378. return `${Util.getMostName(userResolvable)} (${userResolvable.toString()})`;
  2379. };
  2380.  
  2381. exports.fieldsToDesc = function (fields) {
  2382. return `​\n${fields.filter(fieldData => fieldData.name != null).map(fieldData => `**${fieldData.name}${fieldData.value != null ? ': ' : ''}**${fieldData.value != null ? fieldData.value : ''}`).join('\n\n')}`;
  2383. };
  2384.  
  2385. exports.resolveUser = function (guild, userResolvable, canBeSystem) { // If user can be system, userResolvable as text would be the bot/system
  2386. if (userResolvable == null) return undefined;
  2387.  
  2388. const resolvedData = {
  2389. member: userResolvable,
  2390. user: userResolvable,
  2391. id: userResolvable,
  2392. mention: userResolvable,
  2393. original: userResolvable,
  2394. };
  2395.  
  2396. let userType = 0; // Member
  2397. let system = false;
  2398.  
  2399. let wasId = false;
  2400.  
  2401. if (typeof userResolvable === 'string') {
  2402. const idMatch = exports.isId(userResolvable);
  2403. if (idMatch) {
  2404. userType = 1; // ID
  2405. resolvedData.id = idMatch;
  2406. } else {
  2407. userType = 2; // Name or System
  2408. system = canBeSystem && userResolvable.match(/[a-z]/i); // When resolving with system possibility the only use of text should be when the moderator is the system.
  2409. }
  2410. }
  2411.  
  2412. exports.logc('Admin1', `User type: ${userType} (canBeSystem ${canBeSystem || false})`);
  2413.  
  2414. if (userType === 0) { // Member or User
  2415. if (!userResolvable.guild) resolvedData.member = guild.members.get(resolvedData.user.id); // User
  2416. else resolvedData.user = resolvedData.member.user; // Member
  2417. resolvedData.id = resolvedData.user.id;
  2418. resolvedData.mention = exports.resolveMention(resolvedData.member || resolvedData.user);
  2419. } else if (userType === 1) { // Contained ID
  2420. resolvedData.member = guild.members.get(resolvedData.id);
  2421. resolvedData.user = resolvedData.member ? resolvedData.member.user : client.users.get(resolvedData.id);
  2422. if (!resolvedData.user) { // Could be a name imitating an ID
  2423. wasId = true;
  2424. userType = 2;
  2425. } else {
  2426. resolvedData.mention = exports.resolveMention(resolvedData.member || resolvedData.user);
  2427. }
  2428. }
  2429.  
  2430. if (userType === 2) { // Name or System (Separate if statement for branching from userType_1)
  2431. if (system) { // VaeBot
  2432. resolvedData.member = guild.members.get(selfId);
  2433. resolvedData.user = resolvedData.member.user;
  2434. resolvedData.id = selfId;
  2435. } else { // Name
  2436. resolvedData.member = exports.getMemberByMixed(userResolvable, guild);
  2437. resolvedData.user = resolvedData.member ? resolvedData.member.user : exports.getUserByName(userResolvable);
  2438. if (resolvedData.user) {
  2439. resolvedData.id = resolvedData.user.id;
  2440. resolvedData.mention = exports.resolveMention(resolvedData.member || resolvedData.user);
  2441. } else if (!wasId) { // Didn't branch from id
  2442. return 'User not found'; // No user or member
  2443. }
  2444. }
  2445. }
  2446.  
  2447. return resolvedData; // [Definite Values] ID: Always | Mention: Always | Member/User: All inputs except ID and Name
  2448. };
  2449.  
  2450. exports.onFetch = function (messagesParam, channel, leftParam, store) {
  2451. let messages = messagesParam;
  2452. let left = leftParam;
  2453.  
  2454. messages = messages.array();
  2455.  
  2456. if (!messages.length) return Promise.resolve();
  2457.  
  2458. for (let i = 0; i < messages.length; i++) {
  2459. store.push(messages[i]);
  2460. }
  2461.  
  2462. left -= messages.length;
  2463.  
  2464. Util.log(`Received ${messages.length}, left: ${left}`);
  2465.  
  2466. if (left <= 0) return Promise.resolve();
  2467.  
  2468. return exports.fetchMessagesEx(channel, left, store, messages[messages.length - 1]);
  2469. };
  2470.  
  2471. exports.updateMessageCache = function (channel, speaker) {
  2472. exports.fetchMessagesEx(channel, 100, [], channel.messages[0]).then(() => {
  2473. if (speaker) {
  2474. exports.sendDescEmbed(channel, 'Message Cache', 'Refreshed', exports.makeEmbedFooter(speaker), null, colGreen);
  2475. }
  2476. });
  2477. };
  2478.  
  2479. exports.isMember = function (userRes) {
  2480. if (userRes.user != null) return true;
  2481. return false;
  2482. };
  2483.  
  2484. exports.getUser = function (userRes) {
  2485. if (!userRes) return null;
  2486. return userRes.user || userRes;
  2487. };
  2488.  
  2489. exports.isMap = function (obj) {
  2490. return obj instanceof Map;
  2491. };
  2492.  
  2493. exports.arrayToCollection = function (arr) {
  2494. const newCol = new Discord.Collection();
  2495. for (let i = 0; i < arr.length; i++) {
  2496. const value = arr[i];
  2497. newCol.set(value.id, value);
  2498. }
  2499. return newCol;
  2500. };
  2501.  
  2502. exports.chunkObj = function (obj, chunkSize) {
  2503. const chunks = [];
  2504. if (exports.isMap(obj)) {
  2505. const objArray = obj.array();
  2506. const size = obj.size;
  2507. for (let i = 0; i < size; i += chunkSize) {
  2508. const chunkArr = objArray.slice(i, i + chunkSize);
  2509. const chunk = exports.arrayToCollection(chunkArr);
  2510. chunks.push(chunk);
  2511. }
  2512. } else {
  2513. const size = obj.length;
  2514. for (let i = 0; i < size; i += chunkSize) {
  2515. const chunk = obj.slice(i, i + chunkSize);
  2516. chunks.push(chunk);
  2517. }
  2518. }
  2519. return chunks;
  2520. };
  2521.  
  2522. exports.deleteMessages = function (messages) {
  2523. let numMessages;
  2524. let firstMessage;
  2525.  
  2526. if (exports.isMap(messages)) {
  2527. numMessages = messages.size;
  2528. firstMessage = messages.first();
  2529. } else {
  2530. numMessages = messages.length;
  2531. firstMessage = messages[0];
  2532. }
  2533.  
  2534. if (numMessages < 1) {
  2535. Util.log('You must have at least 1 message to delete');
  2536. } else {
  2537. Util.log(`Deleting ${numMessages} messages`);
  2538. }
  2539.  
  2540. if (numMessages == 1) {
  2541. firstMessage.delete()
  2542. .catch((err) => {
  2543. Util.log(`[E_DeleteMessages1] ${err}`);
  2544. });
  2545. } else {
  2546. const chunks = exports.chunkObj(messages, 99);
  2547. for (let i = 0; i < chunks.length; i++) {
  2548. const chunk = chunks[i];
  2549. firstMessage.channel.bulkDelete(chunk)
  2550. .catch((err) => {
  2551. Util.log(`[E_DeleteMessages2] ${err}`);
  2552. });
  2553. }
  2554. }
  2555. };
  2556.  
  2557. async function fetchMessagesInner(channel, remaining, foundMessages, lastMessage) {
  2558. lastMessage = lastMessage != null ? lastMessage.id : undefined;
  2559.  
  2560. const messages = await channel.fetchMessages({ limit: Math.min(remaining, 99), before: lastMessage });
  2561.  
  2562. if (!messages || messages.size == 0) return foundMessages;
  2563.  
  2564. const messagesArr = messages.array();
  2565.  
  2566. for (let i = 0; i < messagesArr.length; i++) {
  2567. foundMessages.push(messagesArr[i]);
  2568. }
  2569.  
  2570. remaining -= messagesArr.length;
  2571.  
  2572. if (remaining <= 0) return foundMessages;
  2573.  
  2574. return fetchMessagesInner(channel, remaining, foundMessages, messagesArr[messagesArr.length - 1]);
  2575. }
  2576.  
  2577. exports.fetchMessages = async function (channel, numScan, checkFunc) {
  2578. if (!checkFunc) checkFunc = (() => true);
  2579.  
  2580. const scanMessages = await fetchMessagesInner(channel, numScan, [], null);
  2581. const foundMessages = scanMessages.filter(checkFunc);
  2582. Util.log(`Num Messages Found: ${foundMessages.length}`);
  2583. return foundMessages;
  2584. };
  2585.  
  2586. exports.banMember = function (member, moderator, reason, tempEnd) {
  2587. const guild = member.guild;
  2588. const memberId = member.id;
  2589. const memberMostName = exports.getMostName(member);
  2590.  
  2591. if (reason == null || reason.length < 1) reason = 'No reason provided';
  2592.  
  2593. let modFullName = moderator;
  2594. if (exports.isObject(moderator)) modFullName = exports.getFullName(moderator);
  2595.  
  2596. const linkedGuilds = Data.getLinkedGuilds(member.guild);
  2597.  
  2598. for (let i = 0; i < linkedGuilds.length; i++) {
  2599. const linkedGuild = linkedGuilds[i];
  2600. linkedGuild.ban(member.id, { days: 0, reason })
  2601. .then((userResolvable) => {
  2602. Util.logc('AddBan1', `Link-added ban for ${exports.getMention(userResolvable, true)} @ ${linkedGuild.name}`);
  2603. })
  2604. .catch(exports.logErr);
  2605. }
  2606.  
  2607. const sendLogData = [
  2608. `Guild ${tempEnd ? 'Temporary ' : ''}Ban`,
  2609. guild,
  2610. member,
  2611. { name: 'Username', value: member.toString() },
  2612. { name: 'Moderator', value: member.toString() },
  2613. { name: 'Ban Reason', value: reason },
  2614. ];
  2615.  
  2616. if (tempEnd) sendLogData.push({ name: 'Ban Ends', value: tempEnd });
  2617.  
  2618. exports.sendLog(sendLogData, colAction);
  2619.  
  2620. // Trello.addCard(member.guild, 'Bans', memberMostName, {
  2621. // 'User ID': memberId,
  2622. // 'Moderator': modFullName,
  2623. // 'Reason': `[TempBan] ${reason}`,
  2624. // });
  2625.  
  2626. return true;
  2627. };
  2628.  
  2629. exports.kickMember = function (member, moderator, reason) {
  2630. // const memberId = member.id;
  2631. // const memberMostName = exports.getMostName(member);
  2632.  
  2633. if (reason == null || reason.length < 1) reason = 'No reason provided';
  2634.  
  2635. // let modFullName = moderator;
  2636. // if (exports.isObject(moderator)) modFullName = exports.getFullName(moderator);
  2637.  
  2638. member.kick()
  2639. .catch(console.error);
  2640.  
  2641. // Trello.addCard(member.guild, 'Kicks', memberMostName, {
  2642. // 'User ID': memberId,
  2643. // 'Moderator': modFullName,
  2644. // 'Reason': reason,
  2645. // });
  2646. };
  2647.  
  2648. exports.getChanges = function (str1, str2) {
  2649. const len1 = str1.length;
  2650. const len2 = str2.length;
  2651. const matrix = []; // len1+1, len2+1
  2652.  
  2653. if (len1 == 0) {
  2654. return len2;
  2655. } else if (len2 == 0) {
  2656. return len1;
  2657. } else if (str1 == str2) {
  2658. return 0;
  2659. }
  2660.  
  2661. for (let i = 0; i <= len1; i++) {
  2662. matrix[i] = {};
  2663. matrix[i][0] = i;
  2664. }
  2665.  
  2666. for (let j = 0; j <= len2; j++) {
  2667. matrix[0][j] = j;
  2668. }
  2669.  
  2670. for (let i = 1; i <= len1; i++) {
  2671. for (let j = 1; j <= len2; j++) {
  2672. let cost = 1;
  2673.  
  2674. if (str1[i - 1] == str2[j - 1]) {
  2675. cost = 0;
  2676. }
  2677.  
  2678. matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
  2679. }
  2680. }
  2681.  
  2682. return matrix[len1][len2];
  2683. };
  2684.  
  2685. exports.getLines = function (str) {
  2686. return str.split(/\r\n|\r|\n/);
  2687. };
  2688.  
  2689. exports.getLines2 = function (str) {
  2690. return exports.chunkString(str, 153); // Take 153 characters as average line length on average 1080p window
  2691. };
  2692.  
  2693. exports.simplifyStr = function (str) {
  2694. str = str.toLowerCase();
  2695. const strLength = str.length;
  2696. const midPoint = (str.length / 2) + 1;
  2697. for (let i = 1; i < midPoint; i++) { // Increment for number of characters in the string stopping before the last (no need to check if whole string is a repetition of itself)
  2698. const sub = str.substr(0, i); // Get the substring from start of length i
  2699. const num = Math.floor(strLength / i); // Get the number of times i goes into the length of the substring (number of times to repeat sub to make it fit)
  2700. const repeatedSub = sub.repeat(num); // Repeat the substring floor(num) times
  2701. if (repeatedSub == str) return [sub, num]; // If repeatedSub is equal to original string, return substring and repetition count
  2702. }
  2703. return [str, 1]; // Return substring and repetition count
  2704. };
  2705.  
  2706. exports.simplifyStrHeavy = function (str) {
  2707. // Assume str is already lowercase
  2708. str = str.replace(/\s/g, '');
  2709. const strLength = str.length;
  2710. const midPoint = (str.length / 2) + 1; // The first int x for which floor(strLength / x) is 1, a.k.a the length when a substring is too large to repeat and fit into str
  2711. let numCanChange = 0;
  2712. let nextInc = 2;
  2713. for (let i = 1; i < midPoint; i++) { // Increments for number of characters in the string stopping before the midpoint
  2714. const sub = str.substr(0, i); // Get the str substring of length i
  2715. const num = Math.floor(strLength / i); // Get the number of times i goes into the length of the substring (number of times to repeat sub to make it fit)
  2716. const repeatedSub = sub.repeat(num); // Repeat the substring num times
  2717. const nowMaxChanges = Math.min(numCanChange * num, strLength / 2); // Get number of allowed alterations between strings to be classed as similar
  2718. if (exports.getChanges(repeatedSub, str) <= nowMaxChanges) return [sub, num]; // If repeatedSub is similar to original string, return substring and repetition count
  2719. if (i >= nextInc) { // Update multiplier for nowMaxChanges when length is large enough
  2720. numCanChange++;
  2721. nextInc *= 2;
  2722. }
  2723. }
  2724. return [str, 1]; // Return substring and repetition count
  2725. };
  2726.  
  2727. exports.similarStrings = function (str1, str2) {
  2728. str1 = str1.toLowerCase().trim();
  2729. str2 = str2.toLowerCase().trim();
  2730.  
  2731. // Get number of allowed alterations between strings to be classed as similar
  2732. let maxChanges = Math.floor(Math.min(Math.max(Math.max(str1.length, str2.length) / 3, Math.abs(str2.length - str1.length)), 6));
  2733.  
  2734. // Check if the original strings are similar (have a number of alterations between them [levenshtein distance] less/equal to maxChanges)
  2735. if (exports.getChanges(str1, str2) <= maxChanges) return true;
  2736.  
  2737. // Simplify both strings removing repeated similar data
  2738. [str1] = exports.simplifyStrHeavy(str1); // Reduce similar repeated strings (e.g. dog1dog2dog3 becomes dog1)
  2739. [str2] = exports.simplifyStrHeavy(str2);
  2740.  
  2741. // Update maxChanges for new string lengths
  2742. maxChanges = Math.floor(Math.min(Math.max(Math.max(str1.length, str2.length) / 3, Math.abs(str2.length - str1.length)), 6));
  2743.  
  2744. // Check if simplified strings are similar
  2745. return exports.getChanges(str1, str2) <= maxChanges;
  2746. };
  2747.  
  2748. exports.similarStringsStrict = function (str1, str2) {
  2749. str1 = str1.toLowerCase().trim();
  2750. str2 = str2.toLowerCase().trim();
  2751.  
  2752. if ((str1.length < 4 || str2.length < 4) && (!(str1.length == 3 || str2.length == 3) || (str1.length <= 3 && str2.length <= 3))) return str1 == str2;
  2753.  
  2754. // Get number of allowed alterations between strings to be classed as similar
  2755. const maxChanges = Math.floor(Math.min(Math.max(Math.max(str1.length, str2.length) / 3, Math.abs(str2.length - str1.length)), 6));
  2756.  
  2757. // Check if the original strings are similar (have a number of alterations between them [levenshtein distance] less/equal to maxChanges)
  2758. if (exports.getChanges(str1, str2) <= maxChanges) return true;
  2759.  
  2760. return false;
  2761. };
  2762.  
  2763. exports.isSpam = function (content) {
  2764. if (exports.getLines2(content).length >= 500) return true; // If the message contains too many chunk-lines (so characters) consider it spam
  2765.  
  2766. const strLines = exports.getLines(content);
  2767.  
  2768. if (strLines.length > 1) {
  2769. let numSimilar = 0;
  2770. let mostCommon = strLines[0];
  2771. let numLines = strLines.length;
  2772.  
  2773. for (let i = 1; i < strLines.length; i++) {
  2774. const nowStr = strLines[i];
  2775. if (nowStr.trim().length < 1) {
  2776. numLines--;
  2777. continue;
  2778. }
  2779. const compStr = numSimilar === 0 ? strLines[i - 1] : mostCommon;
  2780. if (exports.similarStrings(nowStr, compStr)) {
  2781. if (numSimilar === 0) mostCommon = nowStr;
  2782. numSimilar++;
  2783. } else if (i === 2 && numSimilar === 0 && exports.similarStrings(nowStr, mostCommon)) {
  2784. numSimilar++;
  2785. }
  2786. }
  2787.  
  2788. if (numSimilar >= 3 || numSimilar == numLines) return true;
  2789. }
  2790.  
  2791. // ////////////////////////////////////////////////////////////////////////////////////////
  2792.  
  2793. const pattern = /\S+/g; // Pattern for finding all matches for continuous substrings of non space characters
  2794. const matches = content.match(pattern); // Get the matches
  2795.  
  2796. for (let i = 0; i < matches.length; i++) { // Iterate through the matches
  2797. // Util.log(`---${i + 1}---`);
  2798. for (let j = 0; j < matches.length; j++) { // Iterate through the matches again in each iteration for concatenating multiple adjacent matches
  2799. let long = matches[j]; // Get the substring on non space characters
  2800. if (j + i >= matches.length) continue; // If there isn't a match at index j+i it can't be concatenated to joined-substring so skip
  2801. for (let k = 1; k <= i; k++) long += matches[j + k]; // Concatenate all matches after the one at j, onto the match at j, up until (inclusive) the match at i
  2802. // Util.log(long);
  2803. const [sub, num] = exports.simplifyStr(long); // Simplify the resultant concatenated substring that is made up of the match at j and the following matched substrings, to see if it consists of one repeated substring
  2804. // sub: The substring that can be repeated to make up the long var
  2805. // num: The number of times the substring needs to be repeated to make up the long var
  2806. const subLength = sub.length; // The number of characters in the repeated substring
  2807. let triggered = false; // Initialise spam detection variable
  2808. if (num >= 3) { // Only check for spam if substring has been repeated at least 3 times
  2809. if (subLength == 1) { // 1 character in substring, 100+ repetitions
  2810. if (num >= 100) triggered = true; // Is spam
  2811. } else if (subLength == 2) { // 2 characters in substring, 20+ repetitions
  2812. if (num >= 20) triggered = true; // Is spam
  2813. } else if (subLength == 3) { // 3 characters in substring, 7+ repetitions
  2814. if (num >= 7) triggered = true; // Is spam
  2815. } else if (subLength <= 5) { // 4-5 characters in substring, 4+ repetitions
  2816. if (num >= 4) triggered = true; // Is spam
  2817. } else { // 6+ characters in substring, 3+ repetitions
  2818. triggered = true; // Is spam
  2819. }
  2820. }
  2821. if (triggered) { // If it was counted as spam
  2822. Util.log(long, ':', sub, ':', num);
  2823. return true; // Return true (spam)
  2824. }
  2825. }
  2826. }
  2827.  
  2828. return false; // Return false (not spam)
  2829. };
  2830.  
  2831. exports.reverse = function (str) {
  2832. return str.split('').reverse().join('');
  2833. };
  2834.  
  2835. let lastTag = null;
  2836. let lastWasEmpty = true;
  2837.  
  2838. function postOutString(args, startNewline) {
  2839. const nowDate = new Date();
  2840. nowDate.setHours(nowDate.getHours() + 1);
  2841.  
  2842. let out = (startNewline && !lastWasEmpty) ? '\n' : '';
  2843. out += NodeUtil.format(...args);
  2844.  
  2845. let outIndex = out.search(/[^\n\r]/g);
  2846. if (outIndex === -1) outIndex = 0;
  2847.  
  2848. out = out.slice(0, outIndex) + DateFormat(nowDate, '| dd/mm/yyyy | HH:MM | ') + out.slice(outIndex);
  2849.  
  2850. console.log(out);
  2851.  
  2852. lastWasEmpty = /[\n\r]\s*$/.test(out);
  2853. }
  2854.  
  2855. exports.log = function (...args) {
  2856. postOutString(args, true);
  2857. lastTag = null;
  2858. };
  2859.  
  2860. exports.logc = function (...args) {
  2861. const nowTag = String(args.splice(0, 1)).toLowerCase();
  2862. const isNew = lastTag != nowTag;
  2863. postOutString(args, isNew);
  2864. lastTag = nowTag;
  2865. };
  2866.  
  2867. exports.logn = function (...args) {
  2868. postOutString(args, false);
  2869. lastTag = null;
  2870. };
  2871.  
  2872. exports.logErr = function (...args) {
  2873. args.unshift('[ERROR]');
  2874. postOutString(args, true);
  2875. lastTag = null;
  2876. };
  2877.  
  2878. const getAuditLogChunk = 1;
  2879. const getAuditLogMax = 4; // This is completely pointless ...?
  2880.  
  2881. async function getAuditLogRec(guild, auditLogOptions, userData, checkedLogs) {
  2882. if (checkedLogs.length >= getAuditLogMax) return null;
  2883. const entries = (await guild.fetchAuditLogs(auditLogOptions)).entries;
  2884. const outLog = entries.find(log => userData.nowTimestamp - log.createdTimestamp < userData.maxElapsed && log.target.id === userData.target.id); // Needs to check latest first
  2885. if (outLog) return outLog;
  2886. entries.forEach((log) => { if (!checkedLogs.includes(log.id)) checkedLogs.push(log.id); });
  2887. auditLogOptions.limit++;
  2888. userData.maxElapsed += 700;
  2889. return getAuditLogRec(guild, auditLogOptions, userData, checkedLogs);
  2890. }
  2891.  
  2892. exports.getAuditLog = async function (guild, type, userData) {
  2893. userData.executor = Util.resolveUser(guild, userData.executor);
  2894.  
  2895. if (!userData.target) {
  2896. const auditLogOptions = { type, user: userData.executor, limit: 1 };
  2897. return (await guild.fetchAuditLogs(auditLogOptions)).entries.first();
  2898. }
  2899.  
  2900. userData.target = Util.resolveUser(guild, userData.target);
  2901. userData.maxElapsed = userData.maxElapsed || 3000;
  2902.  
  2903. const nowTimestamp = +new Date();
  2904. userData.nowTimestamp = nowTimestamp;
  2905.  
  2906. const auditLogOptions = { type, user: userData.executor, limit: getAuditLogChunk };
  2907. return getAuditLogRec(guild, auditLogOptions, userData, []);
  2908. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement