newMessage.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. const {Util} = require('discord.js');
  2. const {limit: {command: commandLimit}, defaultSettings, wikiProjects} = require('./default.json');
  3. const Wiki = require('./wiki.js');
  4. const check_wiki = {
  5. fandom: require('../cmds/wiki/fandom/general.js'),
  6. general: require('../cmds/wiki/general.js'),
  7. test: require('../cmds/test.js').run
  8. };
  9. const fs = require('fs');
  10. var cmdmap = {};
  11. var pausecmdmap = {};
  12. var ownercmdmap = {};
  13. fs.readdir( './cmds', (error, files) => {
  14. if ( error ) return error;
  15. files.filter( file => file.endsWith('.js') ).forEach( file => {
  16. var command = require('../cmds/' + file);
  17. if ( command.everyone ) cmdmap[command.name] = command.run;
  18. if ( command.pause ) pausecmdmap[command.name] = command.run;
  19. if ( command.owner ) ownercmdmap[command.name] = command.run;
  20. } );
  21. } );
  22. /**
  23. * Processes new messages.
  24. * @param {import('discord.js').Message} msg - The Discord message.
  25. * @param {import('./i18n.js')} lang - The user language.
  26. * @param {Wiki} [wiki] - The default wiki.
  27. * @param {String} [prefix] - The prefix for the message.
  28. * @param {Boolean} [noInline] - Parse inline commands?
  29. * @param {String} [content] - Overwrite for the message content.
  30. */
  31. function newMessage(msg, lang, wiki = defaultSettings.wiki, prefix = process.env.prefix, noInline = null, content = '') {
  32. wiki = new Wiki(wiki);
  33. msg.noInline = noInline;
  34. var cont = ( content || msg.content );
  35. var cleanCont = ( content && Util.cleanContent(content, msg) || msg.cleanContent );
  36. var author = msg.author;
  37. var channel = msg.channel;
  38. if ( msg.isOwner() && cont.hasPrefix(prefix) ) {
  39. let invoke = cont.substring(prefix.length).split(' ')[0].split('\n')[0].toLowerCase();
  40. let aliasInvoke = ( lang.aliases[invoke] || invoke );
  41. if ( aliasInvoke in ownercmdmap ) {
  42. cont = cont.substring(prefix.length);
  43. let args = cont.split(' ').slice(1);
  44. if ( cont.split(' ')[0].split('\n')[1] ) args.unshift( '', cont.split(' ')[0].split('\n')[1] );
  45. console.log( ( channel.isGuild() ? msg.guild.id : '@' + author.id ) + ': ' + prefix + cont );
  46. return ownercmdmap[aliasInvoke](lang, msg, args, cont, wiki);
  47. }
  48. }
  49. var count = 0;
  50. var maxcount = commandLimit[( msg?.guild?.id in patreons ? 'patreon' : 'default' )];
  51. var breakLines = false;
  52. cleanCont.replace( /\u200b/g, '' ).replace( /(?<!\\)```.+?```/gs, '<codeblock>' ).split('\n').forEach( line => {
  53. if ( line.startsWith( '>>> ' ) ) breakLines = true;
  54. if ( !line.hasPrefix(prefix) || breakLines || count > maxcount ) return;
  55. count++;
  56. if ( count === maxcount ) {
  57. console.log( '- Message contains too many commands!' );
  58. msg.reactEmoji('⚠️');
  59. msg.sendChannelError( lang.get('general.limit', '<@' + author.id + '>'), {allowedMentions:{users:[author.id]}} );
  60. return;
  61. }
  62. line = line.substring(prefix.length);
  63. var invoke = line.split(' ')[0].toLowerCase();
  64. var args = line.split(' ').slice(1);
  65. var aliasInvoke = ( lang.aliases[invoke] || invoke );
  66. var ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
  67. var pausecmd = ( msg.isAdmin() && pause[msg.guild.id] && aliasInvoke in pausecmdmap );
  68. if ( msg.onlyVerifyCommand && !( aliasInvoke === 'verify' || pausecmd || ownercmd ) ) return;
  69. if ( channel.isGuild() && pause[msg.guild.id] && !( pausecmd || ownercmd ) ) {
  70. return console.log( msg.guild.id + ': Paused' );
  71. }
  72. console.log( ( channel.isGuild() ? msg.guild.id : '@' + author.id ) + ': ' + prefix + line );
  73. if ( ownercmd ) return ownercmdmap[aliasInvoke](lang, msg, args, line, wiki);
  74. if ( pausecmd ) return pausecmdmap[aliasInvoke](lang, msg, args, line, wiki);
  75. if ( aliasInvoke in cmdmap ) return cmdmap[aliasInvoke](lang, msg, args, line, wiki);
  76. if ( /^![a-z\d-]{1,50}$/.test(invoke) ) {
  77. return cmdmap.LINK(lang, msg, args.join(' '), new Wiki('https://' + invoke.substring(1) + '.gamepedia.com/'), invoke + ' ');
  78. }
  79. if ( /^\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
  80. let invokeWiki = wiki;
  81. if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.fandom.com/' + invoke.substring(1).split('.')[0] + '/';
  82. else invokeWiki = 'https://' + invoke.substring(1) + '.fandom.com/';
  83. return cmdmap.LINK(lang, msg, args.join(' '), new Wiki(invokeWiki), invoke + ' ');
  84. }
  85. if ( /^\?\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
  86. let invokeWiki = wiki;
  87. if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.wikia.org/' + invoke.substring(2).split('.')[0] + '/';
  88. else invokeWiki = 'https://' + invoke.substring(2) + '.wikia.org/';
  89. return cmdmap.LINK(lang, msg, args.join(' '), new Wiki(invokeWiki), invoke + ' ');
  90. }
  91. if ( /^!!(?:[a-z\d-]{1,50}\.)?[a-z\d-]{1,50}\.[a-z\d-]{1,10}(?:\/|$)/.test(invoke) ) {
  92. let project = wikiProjects.find( project => invoke.split('/')[0].endsWith( project.name ) );
  93. if ( project ) {
  94. let regex = invoke.match( new RegExp( project.regex ) );
  95. if ( regex && invoke === '!!' + regex[1] ) return cmdmap.LINK(lang, msg, args.join(' '), new Wiki('https://' + regex[1] + project.scriptPath), invoke + ' ');
  96. }
  97. }
  98. return cmdmap.LINK(lang, msg, line, wiki);
  99. } );
  100. if ( msg.onlyVerifyCommand ) return;
  101. if ( ( !channel.isGuild() || !pause[msg.guild.id] ) && !noInline && ( cont.includes( '[[' ) || cont.includes( '{{' ) ) ) {
  102. var links = [];
  103. var embeds = [];
  104. var linkcount = 0;
  105. var linkmaxcount = maxcount + 5;
  106. var breakInline = false;
  107. cleanCont.replace( /\u200b/g, '' ).replace( /(?<!\\)```.+?```/gs, '<codeblock>' ).replace( /(?<!\\)`.+?`/gs, '<code>' ).split('\n').forEach( line => {
  108. if ( line.startsWith( '>>> ' ) ) breakInline = true;
  109. if ( line.startsWith( '> ' ) || breakInline ) return;
  110. if ( line.hasPrefix(prefix) || !( line.includes( '[[' ) || line.includes( '{{' ) ) ) return;
  111. if ( line.includes( '[[' ) && line.includes( ']]' ) && linkcount <= linkmaxcount ) {
  112. let regex = new RegExp( '(?<!\\\\)(|\\|\\|)\\[\\[([^' + "<>\\[\\]\\|{}\\x01-\\x1F\\x7F" + ']+)(?<!\\\\)\\]\\]\\1', 'g' );
  113. let entry = null;
  114. while ( ( entry = regex.exec(line) ) !== null ) {
  115. if ( linkcount < linkmaxcount ) {
  116. linkcount++;
  117. console.log( ( channel.isGuild() ? msg.guild.id : '@' + author.id ) + ': ' + entry[0] );
  118. let title = entry[2].split('#')[0];
  119. let section = ( entry[2].includes( '#' ) ? entry[2].split('#').slice(1).join('#') : '' )
  120. links.push({title,section,spoiler:entry[1]});
  121. }
  122. else if ( linkcount === linkmaxcount ) {
  123. linkcount++;
  124. console.log( '- Message contains too many links!' );
  125. msg.reactEmoji('⚠️');
  126. break;
  127. }
  128. }
  129. }
  130. if ( line.includes( '{{' ) && line.includes( '}}' ) && count <= maxcount ) {
  131. let regex = new RegExp( '(?<!\\\\)(|\\|\\|)(?<!\\{)\\{\\{([^' + "<>\\[\\]\\|{}\\x01-\\x1F\\x7F" + ']+)(?<!\\\\)\\}\\}\\1', 'g' );
  132. let entry = null;
  133. while ( ( entry = regex.exec(line) ) !== null ) {
  134. if ( count < maxcount ) {
  135. count++;
  136. console.log( ( channel.isGuild() ? msg.guild.id : '@' + author.id ) + ': ' + entry[0] );
  137. let title = entry[2].split('#')[0];
  138. let section = ( entry[2].includes( '#' ) ? entry[2].split('#').slice(1).join('#') : '' )
  139. embeds.push({title,section,spoiler:entry[1]});
  140. }
  141. else if ( count === maxcount ) {
  142. count++;
  143. console.log( '- Message contains too many links!' );
  144. msg.reactEmoji('⚠️');
  145. break;
  146. }
  147. }
  148. }
  149. } );
  150. if ( links.length ) got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&iwurl=true&titles=' + encodeURIComponent( links.map( link => link.title ).join('|') ) + '&format=json' ).then( response => {
  151. var body = response.body;
  152. if ( response.statusCode !== 200 || !body || !body.query ) {
  153. if ( wiki.noWiki(response.url, response.statusCode) ) {
  154. console.log( '- This wiki doesn\'t exist!' );
  155. msg.reactEmoji('nowiki');
  156. return;
  157. }
  158. console.log( '- ' + response.statusCode + ': Error while following the links: ' + ( body && body.error && body.error.info ) );
  159. return;
  160. }
  161. wiki.updateWiki(body.query.general);
  162. if ( body.query.normalized ) {
  163. body.query.normalized.forEach( title => links.filter( link => link.title === title.from ).forEach( link => link.title = title.to ) );
  164. }
  165. if ( body.query.interwiki ) {
  166. body.query.interwiki.forEach( interwiki => links.filter( link => link.title === interwiki.title ).forEach( link => {
  167. link.url = ( link.section ? interwiki.url.split('#')[0] + Wiki.toSection(link.section) : interwiki.url );
  168. } ) );
  169. }
  170. if ( body.query.pages ) {
  171. var querypages = Object.values(body.query.pages);
  172. querypages.filter( page => page.invalid !== undefined ).forEach( page => links.filter( link => link.title === page.title ).forEach( link => {
  173. links.splice(links.indexOf(link), 1);
  174. } ) );
  175. querypages.filter( page => page.missing !== undefined && page.known === undefined ).forEach( page => links.filter( link => link.title === page.title ).forEach( link => {
  176. if ( ( page.ns === 2 || page.ns === 202 ) && !page.title.includes( '/' ) ) return;
  177. if ( wiki.isMiraheze() && page.ns === 0 && /^Mh:[a-z\d]+:/.test(page.title) ) {
  178. var iw_parts = page.title.split(':');
  179. var iw = new Wiki('https://' + iw_parts[1] + '.miraheze.org/w/');
  180. link.url = iw.toLink(iw_parts.slice(2).join(':'), '', link.section);
  181. return;
  182. }
  183. link.url = wiki.toLink(link.title, 'action=edit&redlink=1');
  184. } ) );
  185. }
  186. if ( links.length ) msg.sendChannel( links.map( link => link.spoiler + '<' + ( link.url || wiki.toLink(link.title, '', link.section) ) + '>' + link.spoiler ).join('\n'), {split:true} );
  187. }, error => {
  188. if ( wiki.noWiki(error.message) ) {
  189. console.log( '- This wiki doesn\'t exist!' );
  190. msg.reactEmoji('nowiki');
  191. }
  192. else {
  193. console.log( '- Error while following the links: ' + error );
  194. }
  195. } );
  196. if ( embeds.length ) got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general' + ( wiki.isFandom() ? '' : '|variables' ) + '&titles=' + encodeURIComponent( embeds.map( embed => embed.title + '|Template:' + embed.title ).join('|') ) + '&format=json' ).then( response => {
  197. var body = response.body;
  198. if ( response.statusCode !== 200 || !body || !body.query ) {
  199. if ( wiki.noWiki(response.url, response.statusCode) ) {
  200. console.log( '- This wiki doesn\'t exist!' );
  201. msg.reactEmoji('nowiki');
  202. return;
  203. }
  204. console.log( '- ' + response.statusCode + ': Error while following the links: ' + ( body && body.error && body.error.info ) );
  205. return;
  206. }
  207. wiki.updateWiki(body.query.general);
  208. if ( body.query.normalized ) {
  209. body.query.normalized.forEach( title => embeds.filter( embed => embed.title === title.from ).forEach( embed => embed.title = title.to ) );
  210. }
  211. if ( body.query.pages ) {
  212. var querypages = Object.values(body.query.pages);
  213. querypages.filter( page => page.invalid !== undefined ).forEach( page => embeds.filter( embed => embed.title === page.title ).forEach( embed => {
  214. embeds.splice(embeds.indexOf(embed), 1);
  215. } ) );
  216. var missing = [];
  217. querypages.filter( page => page.missing !== undefined && page.known === undefined ).forEach( page => embeds.filter( embed => embed.title === page.title ).forEach( embed => {
  218. if ( ( page.ns === 2 || page.ns === 202 ) && !page.title.includes( '/' ) ) return;
  219. if ( wiki.isMiraheze() && page.ns === 0 && /^Mh:[a-z\d]+:/.test(page.title) ) return;
  220. embeds.splice(embeds.indexOf(embed), 1);
  221. if ( page.ns === 0 && !embed.section ) {
  222. var template = querypages.find( template => template.ns === 10 && template.title.split(':').slice(1).join(':') === embed.title );
  223. if ( template && template.missing === undefined ) embed.template = wiki.toLink(template.title);
  224. }
  225. if ( embed.template || !body.query.variables || !body.query.variables.some( variable => variable.toUpperCase() === embed.title ) ) missing.push(embed);
  226. } ) );
  227. if ( missing.length ) {
  228. msg.sendChannel( missing.map( embed => embed.spoiler + '<' + ( embed.template || wiki.toLink(embed.title, 'action=edit&redlink=1') ) + '>' + embed.spoiler ).join('\n'), {split:true} );
  229. }
  230. }
  231. if ( embeds.length ) embeds.forEach( embed => msg.reactEmoji('⏳').then( reaction => {
  232. check_wiki.general(lang, msg, embed.title, wiki, '', reaction, embed.spoiler, new URLSearchParams(), embed.section);
  233. } ) );
  234. }, error => {
  235. if ( wiki.noWiki(error.message) ) {
  236. console.log( '- This wiki doesn\'t exist!' );
  237. msg.reactEmoji('nowiki');
  238. }
  239. else {
  240. console.log( '- Error while following the links: ' + error );
  241. }
  242. } );
  243. }
  244. }
  245. module.exports = newMessage;