gamepedia.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. const htmlparser = require('htmlparser2');
  2. const {MessageEmbed} = require('discord.js');
  3. const extract_desc = require('../../util/extract_desc.js');
  4. const {limit: {interwiki: interwikiLimit}, wikiProjects} = require('../../util/default.json');
  5. const fs = require('fs');
  6. var fn = {
  7. special_page: require('../../functions/special_page.js'),
  8. discussion: require('../../functions/discussion.js')
  9. };
  10. fs.readdir( './cmds/wiki/gamepedia', (error, files) => {
  11. if ( error ) return error;
  12. files.filter( file => file.endsWith('.js') ).forEach( file => {
  13. var command = require('./gamepedia/' + file);
  14. fn[command.name] = command.run;
  15. } );
  16. } );
  17. var minecraft = {};
  18. fs.readdir( './cmds/minecraft', (error, files) => {
  19. if ( error ) return error;
  20. files.filter( file => file.endsWith('.js') ).forEach( file => {
  21. var command = require('../minecraft/' + file);
  22. minecraft[command.name] = command.run;
  23. } );
  24. } );
  25. /**
  26. * Checks a Gamepedia wiki.
  27. * @param {import('../../util/i18n.js')} lang - The user language.
  28. * @param {import('discord.js').Message} msg - The Discord message.
  29. * @param {String} title - The page title.
  30. * @param {String} wiki - The wiki for the page.
  31. * @param {String} cmd - The command at this point.
  32. * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  33. * @param {String} [spoiler] - If the response is in a spoiler.
  34. * @param {String} [querystring] - The querystring for the link.
  35. * @param {String} [fragment] - The section for the link.
  36. * @param {String} [interwiki] - The fallback interwiki link.
  37. * @param {Number} [selfcall] - The amount of followed interwiki links.
  38. */
  39. function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', querystring = '', fragment = '', interwiki = '', selfcall = 0) {
  40. var full_title = title;
  41. if ( title.includes( '#' ) ) {
  42. fragment = title.split('#').slice(1).join('#');
  43. title = title.split('#')[0];
  44. }
  45. if ( /\?\w+=/.test(title) ) {
  46. var querystart = title.search(/\?\w+=/);
  47. querystring = title.substring(querystart + 1) + ( querystring ? '&' + querystring : '' );
  48. title = title.substring(0, querystart);
  49. }
  50. if ( title.length > 250 ) {
  51. title = title.substring(0, 250);
  52. msg.reactEmoji('⚠️');
  53. }
  54. var invoke = title.split(' ')[0].toLowerCase();
  55. var aliasInvoke = ( lang.aliases[invoke] || invoke );
  56. var args = title.split(' ').slice(1);
  57. if ( !msg.notMinecraft && wiki === lang.get('minecraft.link') && ( aliasInvoke in minecraft || invoke.startsWith( '/' ) ) ) {
  58. minecraft.WIKI = this;
  59. if ( aliasInvoke in minecraft ) minecraft[aliasInvoke](lang, msg, args, title, cmd, querystring, fragment, reaction, spoiler);
  60. else minecraft.SYNTAX(lang, msg, invoke.substring(1), args, title, cmd, querystring, fragment, reaction, spoiler);
  61. }
  62. else if ( aliasInvoke === 'random' && !args.join('') && !querystring && !fragment ) fn.random(lang, msg, wiki, reaction, spoiler);
  63. else if ( aliasInvoke === 'overview' && !args.join('') && !querystring && !fragment ) fn.overview(lang, msg, wiki, reaction, spoiler);
  64. else if ( aliasInvoke === 'test' && !args.join('') && !querystring && !fragment ) {
  65. this.test(lang, msg, args, '', wiki);
  66. if ( reaction ) reaction.removeEmoji();
  67. }
  68. else if ( aliasInvoke === 'page' ) {
  69. msg.sendChannel( spoiler + '<' + wiki.toLink(args.join('_'), querystring.toTitle(), fragment) + '>' + spoiler );
  70. if ( reaction ) reaction.removeEmoji();
  71. }
  72. else if ( aliasInvoke === 'diff' && args.join('') && !querystring && !fragment ) fn.diff(lang, msg, args, wiki, reaction, spoiler);
  73. else {
  74. var noRedirect = ( /(?:^|&)redirect=no(?:&|$)/.test(querystring) || /(?:^|&)action=(?!view(?:&|$))/.test(querystring) );
  75. got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|specialpagealiases&iwurl=true' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=pageimages|categoryinfo|pageprops|extracts&piprop=original|name&ppprop=description|displaytitle&explaintext=true&exsectionformat=raw&exlimit=1&titles=%1F' + encodeURIComponent( title.replace( /\x1F/g, '\ufffd' ) ) + '&format=json' ).then( response => {
  76. var body = response.body;
  77. if ( body && body.warnings ) log_warn(body.warnings);
  78. if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
  79. if ( interwiki ) msg.sendChannel( spoiler + ' ' + interwiki.replace( /@(here|everyone)/g, '%40$1' ) + fragment + ' ' + spoiler );
  80. else if ( wiki.noWiki(response.url) || response.statusCode === 410 ) {
  81. console.log( '- This wiki doesn\'t exist!' );
  82. msg.reactEmoji('nowiki');
  83. }
  84. else {
  85. console.log( '- ' + response.statusCode + ': Error while getting the search results: ' + ( body && body.error && body.error.info ) );
  86. msg.sendChannelError( spoiler + '<' + wiki.toLink( ( querystring || fragment || !title ? title : 'Special:Search' ), ( querystring || fragment || !title ? querystring.toTitle() : 'search=' + title.toSearch() ), fragment) + '>' + spoiler );
  87. }
  88. if ( reaction ) reaction.removeEmoji();
  89. }
  90. else if ( aliasInvoke === 'search' ) {
  91. fn.search(lang, msg, full_title.split(' ').slice(1).join(' '), wiki, body.query, reaction, spoiler);
  92. }
  93. else if ( aliasInvoke === 'discussion' && wiki.isFandom() && !querystring && !fragment ) {
  94. fn.discussion(lang, msg, wiki, args.join(' '), body.query, reaction, spoiler);
  95. }
  96. else {
  97. if ( body.query.pages ) {
  98. var querypages = Object.values(body.query.pages);
  99. var querypage = querypages[0];
  100. if ( body.query.redirects && body.query.redirects[0].from.split(':')[0] === body.query.namespaces['-1']['*'] && body.query.specialpagealiases.filter( sp => ['Mypage','Mytalk','MyLanguage'].includes( sp.realname ) ).map( sp => sp.aliases[0] ).includes( body.query.redirects[0].from.split(':').slice(1).join(':').split('/')[0].replace( / /g, '_' ) ) ) {
  101. querypage.title = body.query.redirects[0].from;
  102. delete body.query.redirects[0].tofragment;
  103. delete querypage.missing;
  104. querypage.ns = -1;
  105. querypage.special = '';
  106. }
  107. var contribs = body.query.namespaces['-1']['*'] + ':' + body.query.specialpagealiases.find( sp => sp.realname === 'Contributions' ).aliases[0] + '/';
  108. if ( ( querypage.ns === 2 || querypage.ns === 202 || querypage.ns === 1200 ) && ( !querypage.title.includes( '/' ) || /^[^:]+:(?:(?:\d{1,3}\.){3}\d{1,3}\/\d{2}|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}\/\d{2,3})$/.test(querypage.title) ) ) {
  109. var userparts = querypage.title.split(':');
  110. querypage.noRedirect = noRedirect;
  111. fn.user(lang, msg, userparts[0].toTitle() + ':', userparts.slice(1).join(':'), wiki, querystring, fragment, querypage, contribs.toTitle(), reaction, spoiler);
  112. }
  113. else if ( querypage.ns === -1 && querypage.title.startsWith( contribs ) && querypage.title.length > contribs.length ) {
  114. var username = querypage.title.split('/').slice(1).join('/');
  115. got.get( wiki + 'api.php?action=query&titles=User:' + encodeURIComponent( username ) + '&format=json' ).then( uresponse => {
  116. var ubody = uresponse.body;
  117. if ( uresponse.statusCode !== 200 || !ubody || ubody.batchcomplete === undefined || !ubody.query ) {
  118. console.log( '- ' + uresponse.statusCode + ': Error while getting the user: ' + ( ubody && ubody.error && ubody.error.info ) );
  119. msg.sendChannelError( spoiler + '<' + wiki.toLink(contribs + username, querystring.toTitle(), fragment, body.query.general) + '>' + spoiler );
  120. if ( reaction ) reaction.removeEmoji();
  121. }
  122. else {
  123. querypage = Object.values(ubody.query.pages)[0];
  124. if ( querypage.ns === 2 ) {
  125. username = querypage.title.split(':').slice(1).join(':');
  126. querypage.title = contribs + username;
  127. delete querypage.missing;
  128. querypage.ns = -1;
  129. querypage.special = '';
  130. querypage.noRedirect = noRedirect;
  131. fn.user(lang, msg, contribs.toTitle(), username, wiki, querystring, fragment, querypage, contribs.toTitle(), reaction, spoiler);
  132. }
  133. else {
  134. msg.reactEmoji('error');
  135. if ( reaction ) reaction.removeEmoji();
  136. }
  137. }
  138. }, error => {
  139. console.log( '- Error while getting the user: ' + error );
  140. msg.sendChannelError( spoiler + '<' + wiki.toLink(contribs + username, querystring.toTitle(), fragment, body.query.general) + '>' + spoiler );
  141. if ( reaction ) reaction.removeEmoji();
  142. } );
  143. }
  144. else if ( ( querypage.missing !== undefined && querypage.known === undefined && !( noRedirect || querypage.categoryinfo ) ) || querypage.invalid !== undefined ) {
  145. got.get( wiki + 'api.php?action=query&prop=pageimages|categoryinfo|pageprops|extracts&piprop=original|name&ppprop=description|displaytitle&explaintext=true&exsectionformat=raw&exlimit=1&generator=search&gsrnamespace=4|12|14|' + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json' ).then( srresponse => {
  146. var srbody = srresponse.body;
  147. if ( srbody && srbody.warnings ) log_warn(srbody.warnings);
  148. if ( srresponse.statusCode !== 200 || !srbody || srbody.batchcomplete === undefined ) {
  149. console.log( '- ' + srresponse.statusCode + ': Error while getting the search results: ' + ( srbody && srbody.error && srbody.error.info ) );
  150. msg.sendChannelError( spoiler + '<' + wiki.toLink('Special:Search', 'search=' + title.toSearch(), '', body.query.general) + '>' + spoiler );
  151. }
  152. else {
  153. if ( !srbody.query ) {
  154. msg.reactEmoji('🤷');
  155. }
  156. else {
  157. querypage = Object.values(srbody.query.pages)[0];
  158. var pagelink = wiki.toLink(querypage.title, querystring.toTitle(), fragment, body.query.general);
  159. var text = '';
  160. var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  161. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  162. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  163. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  164. embed.setTitle( displaytitle );
  165. }
  166. if ( querypage.extract ) {
  167. var extract = extract_desc(querypage.extract, fragment);
  168. embed.setDescription( extract[0] );
  169. if ( extract[2].length ) embed.addField( extract[1], extract[2] );
  170. }
  171. if ( querypage.pageprops && querypage.pageprops.description ) {
  172. var description = htmlToPlain( querypage.pageprops.description );
  173. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  174. embed.setDescription( description );
  175. }
  176. if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
  177. var pageimage = querypage.original.source;
  178. if ( querypage.ns === 6 ) {
  179. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
  180. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
  181. } else embed.setThumbnail( pageimage );
  182. } else embed.setThumbnail( logoToURL(body.query.general) );
  183. var prefix = ( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix );
  184. var linksuffix = ( querystring ? '?' + querystring : '' ) + ( fragment ? '#' + fragment : '' );
  185. if ( title.replace( /\-/g, ' ' ).toTitle().toLowerCase() === querypage.title.replace( /\-/g, ' ' ).toTitle().toLowerCase() ) {
  186. text = '';
  187. }
  188. else if ( !srbody.continue ) {
  189. text = '\n' + lang.get('search.infopage', '`' + prefix + cmd + ( lang.localNames.page || 'page' ) + ' ' + title + linksuffix + '`');
  190. }
  191. else {
  192. text = '\n' + lang.get('search.infosearch', '`' + prefix + cmd + ( lang.localNames.page || 'page' ) + ' ' + title + linksuffix + '`', '`' + prefix + cmd + ( lang.localNames.search || 'search' ) + ' ' + title + linksuffix + '`');
  193. }
  194. if ( querypage.categoryinfo ) {
  195. var category = [lang.get('search.category.content')];
  196. if ( querypage.categoryinfo.size === 0 ) {
  197. category.push(lang.get('search.category.empty'));
  198. }
  199. if ( querypage.categoryinfo.pages > 0 ) {
  200. category.push(lang.get('search.category.pages', querypage.categoryinfo.pages));
  201. }
  202. if ( querypage.categoryinfo.files > 0 ) {
  203. category.push(lang.get('search.category.files', querypage.categoryinfo.files));
  204. }
  205. if ( querypage.categoryinfo.subcats > 0 ) {
  206. category.push(lang.get('search.category.subcats', querypage.categoryinfo.subcats));
  207. }
  208. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  209. else text += '\n\n' + category.join('\n');
  210. }
  211. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, {embed} );
  212. }
  213. }
  214. }, error => {
  215. console.log( '- Error while getting the search results: ' + error );
  216. msg.sendChannelError( spoiler + '<' + wiki.toLink('Special:Search', 'search=' + title.toSearch(), '', body.query.general) + '>' + spoiler );
  217. } ).finally( () => {
  218. if ( reaction ) reaction.removeEmoji();
  219. } );
  220. }
  221. else if ( querypage.ns === -1 ) {
  222. var pagelink = wiki.toLink(querypage.title, querystring.toTitle(), fragment, body.query.general);
  223. var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setThumbnail( logoToURL(body.query.general) );
  224. var specialpage = body.query.specialpagealiases.find( sp => body.query.namespaces['-1']['*'] + ':' + sp.aliases[0].replace( /\_/g, ' ' ) === querypage.title.split('/')[0] );
  225. specialpage = ( specialpage ? specialpage.realname : querypage.title.replace( body.query.namespaces['-1']['*'] + ':', '' ).split('/')[0] ).toLowerCase();
  226. fn.special_page(lang, msg, querypage.title, specialpage, embed, wiki, reaction, spoiler);
  227. }
  228. else if ( querypage.ns === -2 ) {
  229. var filepath = body.query.specialpagealiases.find( sp => sp.realname === 'Filepath' );
  230. var pagelink = wiki.toLink(body.query.namespaces['-1']['*'] + ':' + ( filepath?.aliases?.[0] || 'FilePath' ) + querypage.title.replace( body.query.namespaces['-2']['*'] + ':', '/' ), querystring.toTitle(), fragment, body.query.general);
  231. var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setDescription( '[' + lang.get('search.media') + '](' + wiki.toLink(querypage.title, '', '', body.query.general, true) + ')' );
  232. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pagelink );
  233. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pagelink,name:( spoiler ? 'SPOILER ' : '' ) + querypage.title}] );
  234. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, {embed} );
  235. if ( reaction ) reaction.removeEmoji();
  236. }
  237. else {
  238. var pagelink = wiki.toLink(querypage.title, querystring.toTitle(), ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ), body.query.general);
  239. var text = '';
  240. var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink );
  241. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  242. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  243. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  244. embed.setTitle( displaytitle );
  245. }
  246. if ( querypage.extract ) {
  247. var extract = extract_desc(querypage.extract, ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ));
  248. embed.setDescription( extract[0] );
  249. if ( extract[2].length ) embed.addField( extract[1], extract[2] );
  250. }
  251. if ( querypage.pageprops && querypage.pageprops.description ) {
  252. var description = htmlToPlain( querypage.pageprops.description );
  253. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  254. embed.setDescription( description );
  255. }
  256. if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
  257. var pageimage = querypage.original.source;
  258. if ( querypage.ns === 6 ) {
  259. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
  260. else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
  261. } else embed.setThumbnail( pageimage );
  262. } else embed.setThumbnail( logoToURL(body.query.general) );
  263. if ( querypage.categoryinfo ) {
  264. var category = [lang.get('search.category.content')];
  265. if ( querypage.categoryinfo.size === 0 ) {
  266. category.push(lang.get('search.category.empty'));
  267. }
  268. if ( querypage.categoryinfo.pages > 0 ) {
  269. category.push(lang.get('search.category.pages', querypage.categoryinfo.pages));
  270. }
  271. if ( querypage.categoryinfo.files > 0 ) {
  272. category.push(lang.get('search.category.files', querypage.categoryinfo.files));
  273. }
  274. if ( querypage.categoryinfo.subcats > 0 ) {
  275. category.push(lang.get('search.category.subcats', querypage.categoryinfo.subcats));
  276. }
  277. if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
  278. else text += '\n\n' + category.join('\n');
  279. }
  280. msg.sendChannel( spoiler + '<' + pagelink + '>' + text + spoiler, {embed} );
  281. if ( reaction ) reaction.removeEmoji();
  282. }
  283. }
  284. else if ( body.query.interwiki ) {
  285. if ( msg.channel.type === 'text' && pause[msg.guild.id] ) {
  286. if ( reaction ) reaction.removeEmoji();
  287. console.log( '- Aborted, paused.' );
  288. return;
  289. }
  290. interwiki = body.query.interwiki[0].url;
  291. var maxselfcall = interwikiLimit[( msg?.guild?.id in patreons ? 'patreon' : 'default' )];
  292. if ( selfcall < maxselfcall && /^(?:https?:)?\/\//.test(interwiki) ) {
  293. selfcall++;
  294. var regex = interwiki.match( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.gamepedia\.com(?:\/|$)/ );
  295. if ( regex ) {
  296. let iwtitle = decodeURIComponent( interwiki.replace( regex[0], '' ) ).replace( /\_/g, ' ' );
  297. this.gamepedia(lang, msg, iwtitle, 'https://' + regex[1] + '.gamepedia.com/', '!' + regex[1] + ' ', reaction, spoiler, querystring, fragment, interwiki, selfcall);
  298. return;
  299. }
  300. regex = interwiki.match( /^(?:https?:)?\/\/(([a-z\d-]{1,50})\.(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/([a-z-]{2,12}))?)(?:\/wiki\/|\/?$)/ );
  301. if ( regex ) {
  302. let iwtitle = decodeURIComponent( interwiki.replace( regex[0], '' ) ).replace( /\_/g, ' ' );
  303. this.fandom(lang, msg, iwtitle, 'https://' + regex[1] + '/', ( regex[1].includes( '.wikia.org' ) ? '??' : '?' ) + ( regex[3] ? regex[3] + '.' : '' ) + regex[2] + ' ', reaction, spoiler, querystring, fragment, interwiki, selfcall);
  304. return;
  305. }
  306. let project = wikiProjects.find( project => interwiki.split('/')[2].endsWith( project.name ) );
  307. if ( project ) {
  308. regex = interwiki.match( new RegExp( '^(?:https?:)?//' + project.regex + `(?:${project.articlePath}|/?$)` ) );
  309. if ( regex ) {
  310. let iwtitle = decodeURIComponent( interwiki.replace( regex[0], '' ) ).replace( /\_/g, ' ' );
  311. this.gamepedia(lang, msg, iwtitle, 'https://' + regex[1] + project.scriptPath, cmd + body.query.interwiki[0].iw + ':', reaction, spoiler, querystring, fragment, interwiki, selfcall);
  312. return;
  313. }
  314. }
  315. }
  316. if ( fragment ) fragment = '#' + fragment.toSection();
  317. if ( interwiki.includes( '#' ) ) {
  318. if ( !fragment ) fragment = '#' + interwiki.split('#').slice(1).join('#');
  319. interwiki = interwiki.split('#')[0];
  320. }
  321. if ( querystring ) interwiki += ( interwiki.includes( '?' ) ? '&' : '?' ) + querystring.toTitle();
  322. msg.sendChannel( spoiler + ' ' + interwiki.replace( /@(here|everyone)/g, '%40$1' ) + fragment + ' ' + spoiler ).then( message => {
  323. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  324. } );
  325. if ( reaction ) reaction.removeEmoji();
  326. }
  327. else {
  328. var pagelink = wiki.toLink(body.query.general.mainpage, querystring.toTitle(), fragment, body.query.general);
  329. var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.general.mainpage.escapeFormatting() ).setURL( pagelink ).setThumbnail( logoToURL(body.query.general) );
  330. got.get( wiki + 'api.php?action=query' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=pageprops|extracts&ppprop=description|displaytitle&explaintext=true&exsectionformat=raw&exlimit=1&titles=' + encodeURIComponent( body.query.general.mainpage ) + '&format=json' ).then( mpresponse => {
  331. var mpbody = mpresponse.body;
  332. if ( mpbody && mpbody.warnings ) log_warn(body.warnings);
  333. if ( mpresponse.statusCode !== 200 || !mpbody || mpbody.batchcomplete === undefined || !mpbody.query ) {
  334. console.log( '- ' + mpresponse.statusCode + ': Error while getting the main page: ' + ( mpbody && mpbody.error && mpbody.error.info ) );
  335. } else {
  336. var querypage = Object.values(mpbody.query.pages)[0];
  337. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  338. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  339. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  340. embed.setTitle( displaytitle );
  341. }
  342. if ( querypage.extract ) {
  343. var extract = extract_desc(querypage.extract, fragment);
  344. embed.setDescription( extract[0] );
  345. if ( extract[2].length ) embed.addField( extract[1], extract[2] );
  346. }
  347. if ( querypage.pageprops && querypage.pageprops.description ) {
  348. var description = htmlToPlain( querypage.pageprops.description );
  349. if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
  350. embed.setDescription( description );
  351. }
  352. }
  353. }, error => {
  354. console.log( '- Error while getting the main page: ' + error );
  355. } ).finally( () => {
  356. msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, {embed} );
  357. if ( reaction ) reaction.removeEmoji();
  358. } );
  359. }
  360. }
  361. }, error => {
  362. if ( interwiki ) msg.sendChannel( spoiler + ' ' + interwiki.replace( /@(here|everyone)/g, '%40$1' ) + fragment + ' ' + spoiler );
  363. else if ( wiki.noWiki(error.message) ) {
  364. console.log( '- This wiki doesn\'t exist!' );
  365. msg.reactEmoji('nowiki');
  366. }
  367. else {
  368. console.log( '- Error while getting the search results: ' + error );
  369. msg.sendChannelError( spoiler + '<' + wiki.toLink( ( querystring || fragment || !title ? title : 'Special:Search' ), ( querystring || fragment || !title ? querystring.toTitle() : 'search=' + title.toSearch() ), fragment) + '>' + spoiler );
  370. }
  371. if ( reaction ) reaction.removeEmoji();
  372. } );
  373. }
  374. }
  375. /**
  376. * Turns the siteinfo logo into an URL.
  377. * @param {Object} arg - The siteinfo from the wiki.
  378. * @param {String} arg.logo - The logo from the wiki.
  379. * @param {String} arg.server - The server URL from the wiki.
  380. * @returns {String}
  381. */
  382. function logoToURL({logo, server: serverURL}) {
  383. if ( /^(?:https?:)?\/\//.test(logo) ) logo = logo.replace( /^(?:https?:)?\/\//, 'https://' );
  384. else logo = serverURL + ( logo.startsWith( '/' ) ? '' : '/' ) + logo;
  385. return logo;
  386. }
  387. /**
  388. * Change HTML text to plain text.
  389. * @param {String} html - The text in HTML.
  390. * @returns {String}
  391. */
  392. function htmlToPlain(html) {
  393. var text = '';
  394. var parser = new htmlparser.Parser( {
  395. ontext: (htmltext) => {
  396. text += htmltext.escapeFormatting();
  397. }
  398. }, {decodeEntities:true} );
  399. parser.write( html );
  400. parser.end();
  401. return text;
  402. };
  403. /**
  404. * Change HTML text to markdown text.
  405. * @param {String} html - The text in HTML.
  406. * @returns {String}
  407. */
  408. function htmlToDiscord(html) {
  409. var text = '';
  410. var parser = new htmlparser.Parser( {
  411. onopentag: (tagname, attribs) => {
  412. switch (tagname) {
  413. case 'b':
  414. text += '**';
  415. break;
  416. case 'i':
  417. text += '*';
  418. break;
  419. case 's':
  420. text += '~~';
  421. break;
  422. case 'u':
  423. text += '__';
  424. break;
  425. }
  426. },
  427. ontext: (htmltext) => {
  428. text += htmltext.escapeFormatting();
  429. },
  430. onclosetag: (tagname) => {
  431. switch (tagname) {
  432. case 'b':
  433. text += '**';
  434. break;
  435. case 'i':
  436. text += '*';
  437. break;
  438. case 's':
  439. text += '~~';
  440. break;
  441. case 'u':
  442. text += '__';
  443. break;
  444. }
  445. }
  446. }, {decodeEntities:true} );
  447. parser.write( html );
  448. parser.end();
  449. return text;
  450. };
  451. module.exports = gamepedia_check_wiki;