general.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. import { readdir } from 'fs';
  2. import { MessageEmbed } from 'discord.js';
  3. import parse_page from '../../functions/parse_page.js';
  4. import phabricator from '../../functions/phabricator.js';
  5. import logging from '../../util/logging.js';
  6. import { got, htmlToDiscord, escapeFormatting, partialURIdecode, breakOnTimeoutPause } from '../../util/functions.js';
  7. import extract_desc from '../../util/extract_desc.js';
  8. import Wiki from '../../util/wiki.js';
  9. import * as fn from './functions.js'
  10. import { createRequire } from 'module';
  11. const require = createRequire(import.meta.url);
  12. const {limit: {interwiki: interwikiLimit}, wikiProjects} = require('../../util/default.json');
  13. const {wikis: mcw} = require('../minecraft/commands.json');
  14. var minecraft = {};
  15. readdir( './cmds/minecraft', (error, files) => {
  16. if ( error ) return error;
  17. files.filter( file => file.endsWith('.js') ).forEach( file => {
  18. import('../minecraft/' + file).then( ({default: command}) => {
  19. minecraft[command.name] = command.run;
  20. } );
  21. } );
  22. } );
  23. /**
  24. * Checks a Gamepedia wiki.
  25. * @param {import('../../util/i18n.js').default} lang - The user language.
  26. * @param {import('discord.js').Message} msg - The Discord message.
  27. * @param {String} title - The page title.
  28. * @param {Wiki} wiki - The wiki for the page.
  29. * @param {String} cmd - The command at this point.
  30. * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  31. * @param {String} [spoiler] - If the response is in a spoiler.
  32. * @param {Boolean} [noEmbed] - If the response should be without an embed.
  33. * @param {URLSearchParams} [querystring] - The querystring for the link.
  34. * @param {String} [fragment] - The section for the link.
  35. * @param {String} [interwiki] - The fallback interwiki link.
  36. * @param {Number} [selfcall] - The amount of followed interwiki links.
  37. */
  38. export default function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', noEmbed = false, querystring = new URLSearchParams(), fragment = '', interwiki = '', selfcall = 0) {
  39. var full_title = title;
  40. if ( title.includes( '#' ) ) {
  41. fragment = title.split('#').slice(1).join('#').trim().replace( /(?:%[\dA-F]{2})+/g, partialURIdecode );
  42. title = title.split('#')[0];
  43. }
  44. if ( /\?\w+=/.test(title) ) {
  45. let querystart = title.search(/\?\w+=/);
  46. querystring = new URLSearchParams(querystring + '&' + title.substring(querystart + 1));
  47. title = title.substring(0, querystart);
  48. }
  49. title = title.replace( /(?:%[\dA-F]{2})+/g, partialURIdecode );
  50. if ( title.length > 250 ) {
  51. title = title.substring(0, 250);
  52. msg.reactEmoji('⚠️');
  53. }
  54. var invoke = full_title.split(' ')[0].toLowerCase();
  55. var aliasInvoke = ( lang.aliases[invoke] || invoke );
  56. var args = full_title.split(' ').slice(1);
  57. if ( aliasInvoke === 'random' && !args.join('') && !querystring.toString() && !fragment ) {
  58. return fn.random(lang, msg, wiki, reaction, spoiler, noEmbed);
  59. }
  60. if ( aliasInvoke === 'overview' && !args.join('') && !querystring.toString() && !fragment ) {
  61. return fn.overview(lang, msg, wiki, reaction, spoiler, noEmbed);
  62. }
  63. if ( aliasInvoke === 'test' && !args.join('') && !querystring.toString() && !fragment ) {
  64. this.test(lang, msg, args, '', wiki);
  65. if ( reaction ) reaction.removeEmoji();
  66. return;
  67. }
  68. if ( aliasInvoke === 'page' ) {
  69. msg.sendChannel( spoiler + '<' + wiki.toLink(args.join('_'), querystring, fragment) + '>' + spoiler );
  70. if ( reaction ) reaction.removeEmoji();
  71. return;
  72. }
  73. if ( aliasInvoke === 'diff' && args.join('') && !querystring.toString() && !fragment ) {
  74. return fn.diff(lang, msg, args, wiki, reaction, spoiler, noEmbed);
  75. }
  76. var noRedirect = ( querystring.getAll('redirect').pop() === 'no' || ( querystring.has('action') && querystring.getAll('action').pop() !== 'view' ) );
  77. var uselang = lang.lang;
  78. if ( querystring.has('variant') || querystring.has('uselang') ) {
  79. uselang = ( querystring.getAll('variant').pop() || querystring.getAll('uselang').pop() || uselang );
  80. lang = lang.uselang(querystring.getAll('variant').pop(), querystring.getAll('uselang').pop());
  81. }
  82. got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|specialpagealiases&iwurl=true' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&converttitles=true&titles=%1F' + encodeURIComponent( ( aliasInvoke === 'search' ? full_title.split(' ').slice(1).join(' ') : title ).replace( /\x1F/g, '\ufffd' ) ) + '&format=json' ).then( response => {
  83. var body = response.body;
  84. if ( body && body.warnings ) log_warning(body.warnings);
  85. if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
  86. if ( interwiki ) msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + interwiki + ( noEmbed ? '>' : ' ' ) + spoiler );
  87. else if ( wiki.noWiki(response.url, response.statusCode) ) {
  88. console.log( '- This wiki doesn\'t exist!' );
  89. msg.reactEmoji('nowiki');
  90. }
  91. else {
  92. console.log( '- ' + response.statusCode + ': Error while getting the search results: ' + ( body && body.error && body.error.info ) );
  93. msg.sendChannelError( spoiler + '<' + wiki.toLink( ( querystring.toString() || fragment || !title ? title : 'Special:Search' ), ( querystring.toString() || fragment || !title ? querystring : {search:title} ), fragment) + '>' + spoiler );
  94. }
  95. if ( reaction ) reaction.removeEmoji();
  96. return;
  97. }
  98. wiki.updateWiki(body.query.general);
  99. if ( aliasInvoke === 'search' ) {
  100. logging(wiki, msg.guildId, 'search');
  101. return fn.search(lang, msg, full_title.split(' ').slice(1).join(' '), wiki, body.query, reaction, spoiler, noEmbed);
  102. }
  103. if ( aliasInvoke === 'discussion' && wiki.isFandom(false) && !querystring.toString() && !fragment ) {
  104. logging(wiki, msg.guildId, 'discussion');
  105. return fn.discussion(lang, msg, wiki, args.join(' '), body.query.general.sitename, reaction, spoiler, noEmbed);
  106. }
  107. if ( !msg.notMinecraft && mcw.hasOwnProperty(wiki.href) && ( minecraft.hasOwnProperty(aliasInvoke) || invoke.startsWith( '/' ) ) && !querystring.toString() && !fragment ) {
  108. logging(wiki, msg.guildId, 'minecraft', ( minecraft.hasOwnProperty(aliasInvoke) ? aliasInvoke : 'command' ));
  109. minecraft.WIKI = this;
  110. if ( minecraft.hasOwnProperty(aliasInvoke) ) minecraft[aliasInvoke](lang, msg, wiki, args, title, cmd, reaction, spoiler, noEmbed);
  111. else minecraft.SYNTAX(lang, msg, wiki, invoke.substring(1), args, title, cmd, reaction, spoiler, noEmbed);
  112. return;
  113. }
  114. if ( body.query.pages && body.query.pages?.['-1']?.title !== '%1F' ) {
  115. var querypages = Object.values(body.query.pages);
  116. var querypage = querypages[0];
  117. 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, '_' ) ) ) {
  118. noRedirect = ( body.query.specialpagealiases.find( sp => sp.realname === 'MyLanguage' )?.aliases?.[0] === body.query.redirects[0].from.split(':').slice(1).join(':').split('/')[0].replace( / /g, '_' ) ? noRedirect : true );
  119. querypage.title = body.query.redirects[0].from;
  120. delete body.query.redirects[0].tofragment;
  121. delete querypage.pageprops;
  122. delete querypage.extract;
  123. delete querypage.pageimage;
  124. delete querypage.original;
  125. delete querypage.missing;
  126. querypage.ns = -1;
  127. querypage.special = '';
  128. querypage.contentmodel = 'wikitext';
  129. }
  130. querypage.uselang = uselang;
  131. querypage.noRedirect = noRedirect;
  132. var contribs = body.query.namespaces['-1']['*'] + ':' + body.query.specialpagealiases.find( sp => sp.realname === 'Contributions' ).aliases[0] + '/';
  133. 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) ) ) {
  134. var userparts = querypage.title.split(':');
  135. querypage.noRedirect = noRedirect;
  136. return fn.user(lang, msg, userparts[0] + ':', userparts.slice(1).join(':'), wiki, querystring, fragment, querypage, contribs, reaction, spoiler, noEmbed);
  137. }
  138. if ( querypage.ns === -1 && querypage.title.startsWith( contribs ) && querypage.title.length > contribs.length ) {
  139. var username = querypage.title.split('/').slice(1).join('/');
  140. return got.get( wiki + 'api.php?action=query&titles=User:' + encodeURIComponent( username ) + '&format=json' ).then( uresponse => {
  141. var ubody = uresponse.body;
  142. if ( uresponse.statusCode !== 200 || !ubody || ubody.batchcomplete === undefined || !ubody.query ) {
  143. console.log( '- ' + uresponse.statusCode + ': Error while getting the user: ' + ( ubody && ubody.error && ubody.error.info ) );
  144. msg.sendChannelError( spoiler + '<' + wiki.toLink(contribs + username, querystring, fragment) + '>' + spoiler );
  145. if ( reaction ) reaction.removeEmoji();
  146. }
  147. else {
  148. querypage = Object.values(ubody.query.pages)[0];
  149. if ( querypage.ns === 2 ) {
  150. username = querypage.title.split(':').slice(1).join(':');
  151. querypage.title = contribs + username;
  152. delete querypage.missing;
  153. querypage.ns = -1;
  154. querypage.special = '';
  155. querypage.uselang = uselang;
  156. querypage.noRedirect = noRedirect;
  157. fn.user(lang, msg, contribs, username, wiki, querystring, fragment, querypage, contribs, reaction, spoiler, noEmbed);
  158. }
  159. else {
  160. msg.reactEmoji('error');
  161. if ( reaction ) reaction.removeEmoji();
  162. }
  163. }
  164. }, error => {
  165. console.log( '- Error while getting the user: ' + error );
  166. msg.sendChannelError( spoiler + '<' + wiki.toLink(contribs + username, querystring, fragment) + '>' + spoiler );
  167. if ( reaction ) reaction.removeEmoji();
  168. } );
  169. }
  170. if ( wiki.isMiraheze() && querypage.ns === 0 && /^Mh:[a-z\d]+:/.test(querypage.title) ) {
  171. logging(wiki, msg.guildId, 'interwiki', 'miraheze');
  172. var iw_parts = querypage.title.split(':');
  173. var iw = new Wiki('https://' + iw_parts[1] + '.miraheze.org/w/');
  174. var iw_link = iw.toLink(iw_parts.slice(2).join(':'), querystring, fragment);
  175. var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
  176. if ( selfcall < maxselfcall ) {
  177. selfcall++;
  178. return this.general(lang, msg, iw_parts.slice(2).join(':'), iw, '!!' + iw.hostname + ' ', reaction, spoiler, noEmbed, querystring, fragment, iw_link, selfcall);
  179. }
  180. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + iw_link + ( noEmbed ? '>' : ' ' ) + spoiler ).then( message => {
  181. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  182. } );
  183. if ( reaction ) reaction.removeEmoji();
  184. return;
  185. }
  186. if ( ( querypage.missing !== undefined && querypage.known === undefined && !( noRedirect || querypage.categoryinfo ) ) || querypage.invalid !== undefined ) return got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&iwurl=true&redirects=true&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&generator=search&gsrnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json' ).then( srresponse => {
  187. logging(wiki, msg.guildId, 'general', 'search');
  188. var srbody = srresponse.body;
  189. if ( srbody?.warnings ) log_warning(srbody.warnings);
  190. if ( srresponse.statusCode !== 200 || !srbody || srbody.batchcomplete === undefined ) {
  191. console.log( '- ' + srresponse.statusCode + ': Error while getting the search results: ' + srbody?.error?.info );
  192. msg.sendChannelError( spoiler + '<' + wiki.toLink('Special:Search', {search:title}) + '>' + spoiler );
  193. if ( reaction ) reaction.removeEmoji();
  194. return;
  195. }
  196. if ( querypage.ns === 12 && wiki.isFandom() ) {
  197. return got.head( wiki.articleURL.href.replace( '$1', encodeURIComponent( querypage.title ).replace( /%3A/g, ':' ) ), {
  198. followRedirect: false
  199. } ).then( hresponse => {
  200. if ( hresponse.statusCode === 301 && /^https:\/\/[a-z\d-]{1,50}\.fandom\.com\/(?:(?!wiki\/)[a-z-]{2,12}\/)?wiki\/Help:/.test( hresponse.headers?.location ) ) {
  201. var location = hresponse.headers.location.split('wiki/');
  202. if ( location[0] === wiki.href && location.slice(1).join('wiki/').replace( /(?:%[\dA-F]{2})+/g, partialURIdecode ).replace( /_/g, ' ' ) === querypage.title ) {
  203. if ( srbody.query ) return srbody;
  204. msg.reactEmoji('🤷');
  205. if ( reaction ) reaction.removeEmoji();
  206. return;
  207. }
  208. var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
  209. if ( selfcall < maxselfcall ) {
  210. selfcall++;
  211. return this.general(lang, msg, location.slice(1).join('wiki/'), new Wiki(location[0]), cmd, reaction, spoiler, noEmbed, querystring, fragment, '', selfcall);
  212. }
  213. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + hresponse.headers.location + ( noEmbed ? '>' : ' ' ) + spoiler ).then( message => {
  214. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  215. } );
  216. if ( reaction ) reaction.removeEmoji();
  217. return;
  218. }
  219. if ( srbody.query ) return srbody;
  220. msg.reactEmoji('🤷');
  221. if ( reaction ) reaction.removeEmoji();
  222. }, error => {
  223. console.log( '- Error while checking the help redirect: ' + error );
  224. if ( srbody.query ) return srbody;
  225. msg.reactEmoji('🤷');
  226. if ( reaction ) reaction.removeEmoji();
  227. } );
  228. }
  229. if ( !srbody.query ) {
  230. return got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&iwurl=true&redirects=true&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&generator=search&gsrwhat=text&gsrnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json' ).then( tsrresponse => {
  231. var tsrbody = tsrresponse.body;
  232. if ( tsrbody?.warnings ) log_warning(tsrbody.warnings);
  233. if ( tsrresponse.statusCode !== 200 || !tsrbody || tsrbody.batchcomplete === undefined ) {
  234. if ( tsrbody?.error?.code !== 'search-text-disabled' ) console.log( '- ' + tsrresponse.statusCode + ': Error while getting the text search results: ' + tsrbody?.error?.info );
  235. }
  236. else if ( tsrbody.query ) return tsrbody;
  237. msg.reactEmoji('🤷');
  238. if ( reaction ) reaction.removeEmoji();
  239. }, error => {
  240. console.log( '- Error while getting the text search results: ' + error );
  241. msg.reactEmoji('🤷');
  242. if ( reaction ) reaction.removeEmoji();
  243. } );
  244. }
  245. return srbody;
  246. } ).then( srbody => {
  247. if ( !srbody?.query?.pages ) {
  248. if ( srbody?.query?.interwiki ) {
  249. if ( breakOnTimeoutPause(msg) ) {
  250. if ( reaction ) reaction.removeEmoji();
  251. return;
  252. }
  253. var iw = new URL(srbody.query.interwiki[0].url.replace( /\\/g, '%5C' ).replace( /@(here|everyone)/g, '%40$1' ), wiki);
  254. querystring.forEach( (value, name) => {
  255. iw.searchParams.append(name, value);
  256. } );
  257. if ( fragment ) iw.hash = Wiki.toSection(fragment);
  258. else fragment = iw.hash.substring(1);
  259. if ( /^phabricator\.(wikimedia|miraheze)\.org$/.test(iw.hostname) ) {
  260. return phabricator(lang, msg, wiki, iw, reaction, spoiler, noEmbed);
  261. }
  262. logging(wiki, msg.guildId, 'interwiki');
  263. var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
  264. if ( selfcall < maxselfcall && ['http:','https:'].includes( iw.protocol ) ) {
  265. selfcall++;
  266. if ( iw.hostname.endsWith( '.gamepedia.com' ) ) {
  267. let iwtitle = decodeURIComponent( iw.pathname.substring(1) ).replace( /_/g, ' ' );
  268. cmd = '!' + iw.hostname.replace( '.gamepedia.com', ' ' );
  269. if ( cmd !== '!www ' ) return this.general(lang, msg, iwtitle, new Wiki(iw.origin), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  270. }
  271. if ( iw.hostname.endsWith( '.fandom.com' ) || iw.hostname.endsWith( '.wikia.org' ) ) {
  272. let regex = iw.pathname.match( /^(\/(?!wiki\/)[a-z-]{2,12})?(?:\/wiki\/|\/?$)/ );
  273. if ( regex ) {
  274. let path = ( regex[1] || '' );
  275. let iwtitle = decodeURIComponent( iw.pathname.replace( regex[0], '' ) ).replace( /_/g, ' ' );
  276. cmd = ( iw.hostname.endsWith( '.wikia.org' ) ? '??' : '?' ) + ( path ? path.substring(1) + '.' : '' ) + iw.hostname.replace( /\.(?:fandom\.com|wikia\.org)/, ' ' );
  277. return this.general(lang, msg, iwtitle, new Wiki(iw.origin + path + '/'), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  278. }
  279. }
  280. let project = wikiProjects.find( project => iw.hostname.endsWith( project.name ) );
  281. if ( project ) {
  282. let regex = ( iw.host + iw.pathname ).match( new RegExp( '^' + project.regex + '(?:' + project.articlePath + '|/?$)' ) );
  283. if ( regex ) {
  284. let iwtitle = decodeURIComponent( ( iw.host + iw.pathname ).replace( regex[0], '' ) ).replace( /_/g, ' ' );
  285. cmd = '!!' + regex[1] + ' ';
  286. return this.general(lang, msg, iwtitle, new Wiki('https://' + regex[1] + project.scriptPath), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  287. }
  288. }
  289. }
  290. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + iw + ( noEmbed ? '>' : ' ' ) + spoiler ).then( message => {
  291. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  292. } );
  293. if ( reaction ) reaction.removeEmoji();
  294. return;
  295. }
  296. return;
  297. }
  298. querypage = Object.values(srbody.query.pages)[0];
  299. querypage.uselang = uselang;
  300. var pagelink = wiki.toLink(querypage.title, querystring, fragment);
  301. var text = '';
  302. var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
  303. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  304. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  305. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  306. embed.setTitle( displaytitle );
  307. }
  308. if ( querypage.extract ) {
  309. var extract = extract_desc(querypage.extract, fragment);
  310. embed.backupDescription = extract[0];
  311. if ( extract[1].length && extract[2].length ) {
  312. embed.backupField = {name: extract[1], value: extract[2]};
  313. }
  314. }
  315. if ( querypage.pageprops && querypage.pageprops.description ) {
  316. var description = htmlToDiscord( querypage.pageprops.description );
  317. if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
  318. embed.backupDescription = description;
  319. }
  320. if ( querypage.ns === 6 ) {
  321. var pageimage = ( querypage?.original?.source || wiki.toLink('Special:FilePath/' + querypage.title, {version:Date.now()}) );
  322. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pageimage );
  323. else if ( querypage.title.toLowerCase().endsWith( '.svg' ) && querypage?.original?.width && msg.showEmbed() ) {
  324. embed.setImage( wiki.toLink('Special:FilePath/' + querypage.title, {width:querypage.original.width,version:Date.now()}) );
  325. }
  326. }
  327. else if ( querypage.title === body.query.general.mainpage ) {
  328. embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
  329. }
  330. else if ( querypage.pageimage && querypage.original ) {
  331. embed.setThumbnail( querypage.original.source );
  332. }
  333. else if ( querypage.pageprops && querypage.pageprops.page_image_free ) {
  334. embed.setThumbnail( wiki.toLink('Special:FilePath/' + querypage.pageprops.page_image_free, {version:Date.now()}) );
  335. }
  336. else embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
  337. var prefix = ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix );
  338. var linksuffix = ( querystring.toString() ? '?' + querystring : '' ) + ( fragment ? '#' + fragment : '' );
  339. if ( title.replace( /[_-]/g, ' ' ).toLowerCase() === querypage.title.replace( /-/g, ' ' ).toLowerCase() ) {
  340. text = '';
  341. }
  342. else if ( !srbody.continue ) {
  343. text = '\n' + lang.get('search.infopage', '`' + prefix + cmd + ( lang.localNames.page || 'page' ) + ' ' + title + linksuffix + '`');
  344. }
  345. else {
  346. text = '\n' + lang.get('search.infosearch', '`' + prefix + cmd + ( lang.localNames.page || 'page' ) + ' ' + title + linksuffix + '`', '`' + prefix + cmd + ( lang.localNames.search || 'search' ) + ' ' + title + linksuffix + '`');
  347. }
  348. if ( querypage.categoryinfo ) {
  349. var category = [lang.get('search.category.content')];
  350. if ( querypage.categoryinfo.size === 0 ) {
  351. category.push(lang.get('search.category.empty'));
  352. }
  353. if ( querypage.categoryinfo.pages > 0 ) {
  354. category.push(lang.get('search.category.pages', querypage.categoryinfo.pages.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.pages));
  355. }
  356. if ( querypage.categoryinfo.files > 0 ) {
  357. category.push(lang.get('search.category.files', querypage.categoryinfo.files.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.files));
  358. }
  359. if ( querypage.categoryinfo.subcats > 0 ) {
  360. category.push(lang.get('search.category.subcats', querypage.categoryinfo.subcats.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.subcats));
  361. }
  362. if ( msg.showEmbed() && !noEmbed ) embed.addField( category[0], category.slice(1).join('\n') );
  363. else text += '\n\n' + category.join('\n');
  364. }
  365. return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + text + spoiler, ( noEmbed ? null : embed ), wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ), fragment, pagelink);
  366. }, error => {
  367. logging(wiki, msg.guildId, 'general', 'search');
  368. console.log( '- Error while getting the search results: ' + error );
  369. msg.sendChannelError( spoiler + '<' + wiki.toLink('Special:Search', {search:title}) + '>' + spoiler );
  370. if ( reaction ) reaction.removeEmoji();
  371. } );
  372. if ( querypage.ns === -1 ) {
  373. var specialpage = body.query.specialpagealiases.find( sp => body.query.namespaces['-1']['*'] + ':' + sp.aliases[0].replace( /\_/g, ' ' ) === querypage.title.split('/')[0] );
  374. specialpage = ( specialpage ? specialpage.realname : querypage.title.replace( body.query.namespaces['-1']['*'] + ':', '' ).split('/')[0] ).toLowerCase();
  375. if ( !['mylanguage'].includes( specialpage ) ) {
  376. return fn.special_page(lang, msg, querypage, specialpage, body.query, wiki, querystring, fragment, reaction, spoiler, noEmbed);
  377. }
  378. }
  379. if ( querypage.ns === -2 ) {
  380. logging(wiki, msg.guildId, 'general', 'media');
  381. var filepath = body.query.specialpagealiases.find( sp => sp.realname === 'Filepath' );
  382. var pagelink = wiki.toLink(body.query.namespaces['-1']['*'] + ':' + ( filepath?.aliases?.[0] || 'FilePath' ) + querypage.title.replace( body.query.namespaces['-2']['*'] + ':', '/' ), querystring, fragment);
  383. var embed = null;
  384. if ( !noEmbed ) {
  385. embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setDescription( '[' + lang.get('search.media') + '](' + wiki.toLink(querypage.title, '', '', true) + ')' );
  386. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pagelink );
  387. }
  388. msg.sendChannel( {content: spoiler + '<' + pagelink + '>' + spoiler, embeds: [embed]} );
  389. if ( reaction ) reaction.removeEmoji();
  390. return;
  391. }
  392. logging(wiki, msg.guildId, 'general');
  393. var pagelink = wiki.toLink(querypage.title, querystring, ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ));
  394. var text = '';
  395. var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
  396. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  397. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  398. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  399. embed.setTitle( displaytitle );
  400. }
  401. if ( querypage.extract ) {
  402. var extract = extract_desc(querypage.extract, ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ));
  403. embed.backupDescription = extract[0];
  404. if ( extract[1].length && extract[2].length ) {
  405. embed.backupField = {name: extract[1], value: extract[2]};
  406. }
  407. }
  408. if ( querypage.pageprops && querypage.pageprops.description ) {
  409. var description = htmlToDiscord( querypage.pageprops.description );
  410. if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
  411. embed.backupDescription = description;
  412. }
  413. if ( querypage.ns === 6 ) {
  414. var pageimage = ( querypage?.original?.source || wiki.toLink('Special:FilePath/' + querypage.title, {version:Date.now()}) );
  415. if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pageimage );
  416. }
  417. else if ( querypage.title === body.query.general.mainpage ) {
  418. embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
  419. }
  420. else if ( querypage.pageimage && querypage.original ) {
  421. embed.setThumbnail( querypage.original.source );
  422. }
  423. else if ( querypage.pageprops && querypage.pageprops.page_image_free ) {
  424. embed.setThumbnail( wiki.toLink('Special:FilePath/' + querypage.pageprops.page_image_free, {version:Date.now()}) );
  425. }
  426. else embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
  427. if ( querypage.categoryinfo ) {
  428. var category = [lang.get('search.category.content')];
  429. if ( querypage.categoryinfo.size === 0 ) {
  430. category.push(lang.get('search.category.empty'));
  431. }
  432. if ( querypage.categoryinfo.pages > 0 ) {
  433. category.push(lang.get('search.category.pages', querypage.categoryinfo.pages.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.pages));
  434. }
  435. if ( querypage.categoryinfo.files > 0 ) {
  436. category.push(lang.get('search.category.files', querypage.categoryinfo.files.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.files));
  437. }
  438. if ( querypage.categoryinfo.subcats > 0 ) {
  439. category.push(lang.get('search.category.subcats', querypage.categoryinfo.subcats.toLocaleString(lang.get('dateformat')), querypage.categoryinfo.subcats));
  440. }
  441. if ( msg.showEmbed() && !noEmbed ) embed.addField( category[0], category.slice(1).join('\n') );
  442. else text += '\n\n' + category.join('\n');
  443. }
  444. return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + text + spoiler, ( noEmbed ? null : embed ), wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ), ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ), pagelink);
  445. }
  446. if ( body.query.interwiki ) {
  447. if ( breakOnTimeoutPause(msg) ) {
  448. if ( reaction ) reaction.removeEmoji();
  449. return;
  450. }
  451. var iw = new URL(body.query.interwiki[0].url.replace( /\\/g, '%5C' ).replace( /@(here|everyone)/g, '%40$1' ), wiki);
  452. querystring.forEach( (value, name) => {
  453. iw.searchParams.append(name, value);
  454. } );
  455. if ( fragment ) iw.hash = Wiki.toSection(fragment);
  456. else fragment = iw.hash.substring(1);
  457. if ( /^phabricator\.(wikimedia|miraheze)\.org$/.test(iw.hostname) ) {
  458. return phabricator(lang, msg, wiki, iw, reaction, spoiler, noEmbed);
  459. }
  460. logging(wiki, msg.guildId, 'interwiki');
  461. var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
  462. if ( selfcall < maxselfcall && ['http:','https:'].includes( iw.protocol ) ) {
  463. selfcall++;
  464. if ( iw.hostname.endsWith( '.gamepedia.com' ) ) {
  465. let iwtitle = decodeURIComponent( iw.pathname.substring(1) ).replace( /_/g, ' ' );
  466. cmd = '!' + iw.hostname.replace( '.gamepedia.com', ' ' );
  467. if ( cmd !== '!www ' ) return this.general(lang, msg, iwtitle, new Wiki(iw.origin), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  468. }
  469. if ( iw.hostname.endsWith( '.fandom.com' ) || iw.hostname.endsWith( '.wikia.org' ) ) {
  470. let regex = iw.pathname.match( /^(\/(?!wiki\/)[a-z-]{2,12})?(?:\/wiki\/|\/?$)/ );
  471. if ( regex ) {
  472. let path = ( regex[1] || '' );
  473. let iwtitle = decodeURIComponent( iw.pathname.replace( regex[0], '' ) ).replace( /_/g, ' ' );
  474. cmd = ( iw.hostname.endsWith( '.wikia.org' ) ? '??' : '?' ) + ( path ? path.substring(1) + '.' : '' ) + iw.hostname.replace( /\.(?:fandom\.com|wikia\.org)/, ' ' );
  475. return this.general(lang, msg, iwtitle, new Wiki(iw.origin + path + '/'), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  476. }
  477. }
  478. let project = wikiProjects.find( project => iw.hostname.endsWith( project.name ) );
  479. if ( project ) {
  480. let regex = ( iw.host + iw.pathname ).match( new RegExp( '^' + project.regex + '(?:' + project.articlePath + '|/?$)' ) );
  481. if ( regex ) {
  482. let iwtitle = decodeURIComponent( ( iw.host + iw.pathname ).replace( regex[0], '' ) ).replace( /_/g, ' ' );
  483. cmd = '!!' + regex[1] + ' ';
  484. return this.general(lang, msg, iwtitle, new Wiki('https://' + regex[1] + project.scriptPath), cmd, reaction, spoiler, noEmbed, iw.searchParams, fragment, iw.href, selfcall);
  485. }
  486. }
  487. }
  488. msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + iw + ( noEmbed ? '>' : ' ' ) + spoiler ).then( message => {
  489. if ( message && selfcall === maxselfcall ) message.reactEmoji('⚠️');
  490. } );
  491. if ( reaction ) reaction.removeEmoji();
  492. return;
  493. }
  494. logging(wiki, msg.guildId, 'general');
  495. var querypage = {
  496. title: body.query.general.mainpage,
  497. contentmodel: 'wikitext',
  498. uselang, noRedirect
  499. };
  500. var pagelink = wiki.toLink(querypage.title, querystring, fragment);
  501. var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setThumbnail( new URL(body.query.general.logo, wiki).href );
  502. got.get( wiki + 'api.php?uselang=' + uselang + '&action=query' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=info|pageprops|extracts&ppprop=description|displaytitle|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&titles=' + encodeURIComponent( querypage.title ) + '&format=json' ).then( mpresponse => {
  503. var mpbody = mpresponse.body;
  504. if ( mpbody && mpbody.warnings ) log_warning(body.warnings);
  505. if ( mpresponse.statusCode !== 200 || !mpbody || mpbody.batchcomplete === undefined || !mpbody.query ) {
  506. console.log( '- ' + mpresponse.statusCode + ': Error while getting the main page: ' + ( mpbody && mpbody.error && mpbody.error.info ) );
  507. return;
  508. }
  509. querypage = Object.values(mpbody.query.pages)[0];
  510. if ( mpbody.query.redirects && mpbody.query.redirects[0].from.split(':')[0] === body.query.namespaces['-1']['*'] && body.query.specialpagealiases.filter( sp => ['Mypage','Mytalk'].includes( sp.realname ) ).map( sp => sp.aliases[0] ).includes( mpbody.query.redirects[0].from.split(':').slice(1).join(':').split('/')[0].replace( / /g, '_' ) ) ) {
  511. noRedirect = true;
  512. querypage.title = mpbody.query.redirects[0].from;
  513. delete mpbody.query.redirects[0].tofragment;
  514. delete querypage.pageprops;
  515. delete querypage.extract;
  516. delete querypage.missing;
  517. querypage.ns = -1;
  518. querypage.special = '';
  519. querypage.contentmodel = 'wikitext';
  520. }
  521. querypage.uselang = uselang;
  522. querypage.noRedirect = noRedirect;
  523. if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
  524. var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
  525. if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
  526. embed.setTitle( displaytitle );
  527. }
  528. if ( querypage.extract ) {
  529. var extract = extract_desc(querypage.extract, fragment);
  530. embed.backupDescription = extract[0];
  531. if ( extract[1].length && extract[2].length ) {
  532. embed.backupField = {name: extract[1], value: extract[2]};
  533. }
  534. }
  535. if ( querypage.pageprops && querypage.pageprops.description ) {
  536. var description = htmlToDiscord( querypage.pageprops.description );
  537. if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
  538. embed.backupDescription = description;
  539. }
  540. }, error => {
  541. console.log( '- Error while getting the main page: ' + error );
  542. } ).finally( () => {
  543. parse_page(lang, msg, spoiler + '<' + pagelink + '>' + spoiler, ( noEmbed ? null : embed ), wiki, reaction, querypage, '', fragment, pagelink);
  544. } );
  545. }, error => {
  546. if ( interwiki ) msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + interwiki + ( noEmbed ? '>' : ' ' ) + spoiler );
  547. else if ( wiki.noWiki(error.message) ) {
  548. console.log( '- This wiki doesn\'t exist!' );
  549. msg.reactEmoji('nowiki');
  550. }
  551. else {
  552. console.log( '- Error while getting the search results: ' + error );
  553. msg.sendChannelError( spoiler + '<' + wiki.toLink( ( querystring.toString() || fragment || !title ? title : 'Special:Search' ), ( querystring.toString() || fragment || !title ? querystring : {search:title} ), fragment) + '>' + spoiler );
  554. }
  555. if ( reaction ) reaction.removeEmoji();
  556. } );
  557. }