Prechádzať zdrojové kódy

Add page desc by edit

Markus-Rost 4 rokov pred
rodič
commit
8e6522575c
10 zmenil súbory, kde vykonal 4496 pridanie a 275 odobranie
  1. 1 0
      bot.js
  2. 8 33
      cmds/wiki/general.js
  3. 2 12
      cmds/wiki/random.js
  4. 3 23
      cmds/wiki/user.js
  5. 5 0
      dashboard/i18n/allLangs.json
  6. 258 203
      functions/parse_page.js
  7. 1 0
      i18n/en.json
  8. 4214 1
      package-lock.json
  9. 1 1
      package.json
  10. 3 2
      util/wiki.js

+ 1 - 0
bot.js

@@ -340,6 +340,7 @@ const common_warnings = {
 	],
 	query: [
 		'Unrecognized values for parameter "prop": pageimages, extracts.',
+		'Unrecognized values for parameter "prop": pageimages, extracts',
 		'Unrecognized value for parameter "prop": extracts.',
 		'Unrecognized value for parameter "prop": pageimages.'
 	]

+ 8 - 33
cmds/wiki/general.js

@@ -2,7 +2,7 @@ const {MessageEmbed} = require('discord.js');
 const parse_page = require('../../functions/parse_page.js');
 const phabricator = require('../../functions/phabricator.js');
 const logging = require('../../util/logging.js');
-const {parse_infobox, htmlToDiscord, partialURIdecode} = require('../../util/functions.js');
+const {htmlToDiscord, partialURIdecode} = require('../../util/functions.js');
 const extract_desc = require('../../util/extract_desc.js');
 const {limit: {interwiki: interwikiLimit}, wikiProjects} = require('../../util/default.json');
 const Wiki = require('../../util/wiki.js');
@@ -230,6 +230,10 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				if ( querypage.ns === 6 ) {
 					var pageimage = ( querypage?.original?.source || wiki.toLink('Special:FilePath/' + querypage.title, {version:Date.now()}) );
 					if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pageimage );
+					else if ( querypage.title.toLowerCase().endsWith( '.svg' ) && querypage?.original?.width && msg.showEmbed() ) {
+						embed.setImage( wiki.toLink('Special:FilePath/' + querypage.title, {width:querypage.original.width,version:Date.now()}) );
+						if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.title}] );
+					}
 					else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.title}] );
 				}
 				else if ( querypage.title === body.query.general.mainpage ) {
@@ -273,17 +277,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 					else text += '\n\n' + category.join('\n');
 				}
 
-				if ( !fragment && !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-					try {
-						var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-						parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, pagelink);
-					}
-					catch ( error ) {
-						console.log( '- Failed to parse the infobox: ' + error );
-					}
-				}
-
-				return parse_page(msg, spoiler + '<' + pagelink + '>' + text + spoiler, embed, wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ), fragment);
+				return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + text + spoiler, embed, wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ), fragment, pagelink);
 			}, error => {
 				logging(wiki, msg.guild?.id, 'general', 'search');
 				console.log( '- Error while getting the search results: ' + error );
@@ -364,18 +358,8 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				if ( msg.showEmbed() ) embed.addField( category[0], category.slice(1).join('\n') );
 				else text += '\n\n' + category.join('\n');
 			}
-
-			if ( !( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ) && !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-				try {
-					var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-					parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, pagelink);
-				}
-				catch ( error ) {
-					console.log( '- Failed to parse the infobox: ' + error );
-				}
-			}
 			
-			return parse_page(msg, spoiler + '<' + pagelink + '>' + text + spoiler, 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 ) || '' ));
+			return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + text + spoiler, 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);
 		}
 		if ( body.query.interwiki ) {
 			if ( msg.channel.isGuild() && pause[msg.guild.id] ) {
@@ -455,19 +439,10 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
 				embed.backupDescription = description;
 			}
-			if ( !fragment && !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-				try {
-					var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-					parse_infobox(infobox, embed, '', pagelink);
-				}
-				catch ( error ) {
-					console.log( '- Failed to parse the infobox: ' + error );
-				}
-			}
 		}, error => {
 			console.log( '- Error while getting the main page: ' + error );
 		} ).finally( () => {
-			parse_page(msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, '', fragment);
+			parse_page(lang, msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, '', fragment, pagelink);
 		} );
 	}, error => {
 		if ( interwiki ) msg.sendChannel( spoiler + ' ' + interwiki + ' ' + spoiler );

+ 2 - 12
cmds/wiki/random.js

@@ -2,7 +2,7 @@ const {MessageEmbed} = require('discord.js');
 const fandom_random = require('./fandom/random.js').run;
 const parse_page = require('../../functions/parse_page.js');
 const logging = require('../../util/logging.js');
-const {parse_infobox, htmlToDiscord} = require('../../util/functions.js');
+const {htmlToDiscord} = require('../../util/functions.js');
 const extract_desc = require('../../util/extract_desc.js');
 
 /**
@@ -62,17 +62,7 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler) {
 		}
 		else embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
 		
-		if ( !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-			try {
-				var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-				parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, pagelink);
-			}
-			catch ( error ) {
-				console.log( '- Failed to parse the infobox: ' + error );
-			}
-		}
-		
-		parse_page(msg, '🎲 ' + spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ));
+		parse_page(lang, msg, '🎲 ' + spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ));
 	}, error => {
 		if ( wiki.noWiki(error.message) ) {
 			console.log( '- This wiki doesn\'t exist!' );

+ 3 - 23
cmds/wiki/user.js

@@ -4,7 +4,7 @@ const parse_page = require('../../functions/parse_page.js');
 const logging = require('../../util/logging.js');
 const extract_desc = require('../../util/extract_desc.js');
 const {timeoptions, usergroups} = require('../../util/default.json');
-const {parse_infobox, toMarkdown, toPlaintext, htmlToDiscord} = require('../../util/functions.js');
+const {toMarkdown, toPlaintext, htmlToDiscord} = require('../../util/functions.js');
 
 var allSites = [];
 const getAllSites = require('../../util/allSites.js');
@@ -65,17 +65,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 						embed.setThumbnail( wiki.toLink('Special:FilePath/' + querypage.pageprops.page_image_free, {version:Date.now()}) );
 					}
 					
-					if ( !fragment && !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-						try {
-							var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-							parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, pagelink);
-						}
-						catch ( error ) {
-							console.log( '- Failed to parse the infobox: ' + error );
-						}
-					}
-					
-					return parse_page(msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, new URL(body.query.general.logo, wiki).href, fragment);
+					return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, new URL(body.query.general.logo, wiki).href, fragment, pagelink);
 				}
 			}
 			else {
@@ -244,17 +234,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				}
 				else embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
 				
-				if ( !fragment && !embed.fields.length && querypage.pageprops && querypage.pageprops.infoboxes ) {
-					try {
-						var infobox = JSON.parse(querypage.pageprops.infoboxes)?.[0];
-						parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, pagelink);
-					}
-					catch ( error ) {
-						console.log( '- Failed to parse the infobox: ' + error );
-					}
-				}
-				
-				return parse_page(msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, new URL(body.query.general.logo, wiki).href, fragment);
+				return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + spoiler, embed, wiki, reaction, querypage, new URL(body.query.general.logo, wiki).href, fragment, pagelink);
 			}
 			
 			if ( reaction ) reaction.removeEmoji();

+ 5 - 0
dashboard/i18n/allLangs.json

@@ -5,6 +5,7 @@
 			"bn": "বাংলা (bn)",
 			"de": "Deutsch (de)",
 			"es": "Español (es)",
+			"fr": "Français (fr)",
 			"hi": "हिन्दी (hi)",
 			"ko": "한국어 (ko)",
 			"pl": "Polski (pl)",
@@ -31,6 +32,10 @@
 			"spanish": "es",
 			"español": "es",
 			"español (es)": "es",
+			"fr": "fr",
+			"french": "fr",
+			"français": "fr",
+			"français (fr)": "fr",
 			"hi": "hi",
 			"hindi": "hi",
 			"हिन्दी": "hi",

+ 258 - 203
functions/parse_page.js

@@ -1,6 +1,7 @@
 const cheerio = require('cheerio');
+const {MessageEmbed} = require('discord.js');
 const {toSection} = require('../util/wiki.js');
-const {htmlToPlain, htmlToDiscord, limitLength} = require('../util/functions.js');
+const {parse_infobox, htmlToPlain, htmlToDiscord, limitLength} = require('../util/functions.js');
 
 const parsedContentModels = [
 	'wikitext',
@@ -51,9 +52,11 @@ const removeClasses = [
 	'wb\\:sectionedit'
 ];
 
-const removeClassExceptions = [
+const removeClassesExceptions = [
 	'div.main-page-tag-lcs',
 	'div.lcs-container',
+	'div.poem',
+	'div.treeview',
 	'div.wikibase-entityview',
 	'div.wikibase-entityview-main',
 	'div.wikibase-entitytermsview',
@@ -66,6 +69,7 @@ const removeClassExceptions = [
 
 /**
  * Parses a wiki page to get it's description.
+ * @param {import('../../util/i18n.js')} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} content - The content for the message.
  * @param {import('discord.js').MessageEmbed} embed - The embed for the message.
@@ -75,247 +79,298 @@ const removeClassExceptions = [
  * @param {String} querypage.title - The title of the page.
  * @param {String} querypage.contentmodel - The content model of the page.
  * @param {Object} [querypage.pageprops] - The properties of the page.
+ * @param {String} [querypage.pageprops.infoboxes] - The JSON data for portable infoboxes on the page.
  * @param {String} [querypage.pageprops.disambiguation] - The disambiguation property of the page.
  * @param {String} thumbnail - The default thumbnail for the wiki.
  * @param {String} [fragment] - The section title to embed.
+ * @param {String} [pagelink] - The link to the page.
  */
-function parse_page(msg, content, embed, wiki, reaction, {title, contentmodel, pageprops: {disambiguation} = {}}, thumbnail, fragment = '') {
-	if ( !msg?.showEmbed?.() || ( embed.description && embed.thumbnail?.url !== thumbnail && !embed.brokenInfobox && !fragment ) ) {
+function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmodel, pageprops: {infoboxes, disambiguation} = {}}, thumbnail, fragment = '', pagelink = '') {
+	if ( !msg?.showEmbed?.() ) {
 		msg.sendChannel( content, {embed} );
 
 		if ( reaction ) reaction.removeEmoji();
 		return;
 	}
-	if ( !parsedContentModels.includes( contentmodel ) ) return got.get( wiki + 'api.php?action=query&prop=revisions&rvprop=content&rvslots=main&converttitles=true&titles=%1F' + encodeURIComponent( title ) + '&format=json' ).then( response => {
-		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
-		var revision = Object.values(( body?.query?.pages || {} ))?.[0]?.revisions?.[0];
-		revision = ( revision?.slots?.main || revision );
-		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !revision?.['*'] ) {
-			console.log( '- ' + response.statusCode + ': Error while getting the page content: ' + ( body && body.error && body.error.info ) );
+
+	if ( reaction ) reaction.removeEmoji();
+	Promise.all([
+		msg.sendChannel( content, {
+			embed: new MessageEmbed(embed).setDescription( '<a:loading:641343250661113886> **' + lang.get('search.loading') + '**' )
+		} ),
+		( !parsedContentModels.includes( contentmodel ) ? got.get( wiki + 'api.php?action=query&prop=revisions&rvprop=content&rvslots=main&converttitles=true&titles=%1F' + encodeURIComponent( title ) + '&format=json', {
+			timeout: 10000
+		} ).then( response => {
+			var body = response.body;
+			if ( body && body.warnings ) log_warn(body.warnings);
+			var revision = Object.values(( body?.query?.pages || {} ))?.[0]?.revisions?.[0];
+			revision = ( revision?.slots?.main || revision );
+			if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !revision?.['*'] ) {
+				console.log( '- ' + response.statusCode + ': Error while getting the page content: ' + ( body && body.error && body.error.info ) );
+				if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+					embed.spliceFields( 0, 0, embed.backupField );
+				}
+				if ( embed.backupDescription && embed.length < 5000 ) {
+					embed.setDescription( embed.backupDescription );
+				}
+				return;
+			}
+			if ( !embed.description && embed.length < 4000 ) {
+				var description = revision['*'];
+				var regex = /^L(\d+)(?:-L?(\d+))?$/.exec(fragment);
+				if ( regex ) {
+					let descArray = description.split('\n').slice(regex[1] - 1, ( regex[2] || regex[1] ));
+					if ( descArray.length ) {
+						description = descArray.join('\n').replace( /^\n+/, '' ).replace( /\n+$/, '' );
+						if ( description ) {
+							if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
+							description = '```' + ( contentModels[revision.contentmodel] || contentFormats[revision.contentformat] || '' ) + '\n' + description + '\n```';
+							embed.setDescription( description );
+						}
+					}
+				}
+				else if ( description.trim() ) {
+					description = description.replace( /^\n+/, '' ).replace( /\n+$/, '' );
+					if ( description.length > 500 ) description = description.substring(0, 500) + '\u2026';
+					description = '```' + ( contentModels[revision.contentmodel] || contentFormats[revision.contentformat] || '' ) + '\n' + description + '\n```';
+					embed.setDescription( description );
+				}
+				else if ( embed.backupDescription ) {
+					embed.setDescription( embed.backupDescription );
+				}
+			}
+		}, error => {
+			console.log( '- Error while getting the page content: ' + error );
 			if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
 				embed.spliceFields( 0, 0, embed.backupField );
 			}
 			if ( embed.backupDescription && embed.length < 5000 ) {
 				embed.setDescription( embed.backupDescription );
 			}
-			return;
-		}
-		if ( !embed.description && embed.length < 4000 ) {
-			var description = revision['*'];
-			var regex = /^L(\d+)(?:-L?(\d+))?$/.exec(fragment);
-			if ( regex ) {
-				let descArray = description.split('\n').slice(regex[1] - 1, ( regex[2] || regex[1] ));
-				if ( descArray.length ) {
-					description = descArray.join('\n').replace( /^\n+/, '' ).replace( /\n+$/, '' );
-					if ( description ) {
-						if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
-						description = '```' + ( contentModels[revision.contentmodel] || contentFormats[revision.contentformat] || '' ) + '\n' + description + '\n```';
-						embed.setDescription( description );
-					}
-				}
+		} ) : function() {
+		if ( !fragment && !embed.fields.length && infoboxes ) {
+			try {
+				var infobox = JSON.parse(infoboxes)?.[0];
+				parse_infobox(infobox, embed, thumbnail, embed.url);
 			}
-			else if ( description.trim() ) {
-				description = description.replace( /^\n+/, '' ).replace( /\n+$/, '' );
-				if ( description.length > 500 ) description = description.substring(0, 500) + '\u2026';
-				description = '```' + ( contentModels[revision.contentmodel] || contentFormats[revision.contentformat] || '' ) + '\n' + description + '\n```';
-				embed.setDescription( description );
+			catch ( error ) {
+				console.log( '- Failed to parse the infobox: ' + error );
 			}
-			else if ( embed.backupDescription ) {
-				embed.setDescription( embed.backupDescription );
-			}
-		}
-	}, error => {
-		console.log( '- Error while getting the page content: ' + error );
-		if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
-			embed.spliceFields( 0, 0, embed.backupField );
-		}
-		if ( embed.backupDescription && embed.length < 5000 ) {
-			embed.setDescription( embed.backupDescription );
 		}
-	} ).finally( () => {
-		msg.sendChannel( content, {embed} );
-		
-		if ( reaction ) reaction.removeEmoji();
-	} );
-	got.get( wiki + 'api.php?action=parse&prop=text|images|displaytitle' + ( contentmodel !== 'wikitext' || fragment || disambiguation !== undefined ? '' : '&section=0' ) + '&disablelimitreport=true&disableeditsection=true&disabletoc=true&sectionpreview=true&page=' + encodeURIComponent( title ) + '&format=json' ).then( response => {
-		if ( response.statusCode !== 200 || !response?.body?.parse?.text ) {
-			console.log( '- ' + response.statusCode + ': Error while parsing the page: ' + response?.body?.error?.info );
-			if ( embed.backupDescription && embed.length < 5000 ) {
-				embed.setDescription( embed.backupDescription );
+		return got.get( wiki + 'api.php?action=parse&prop=text|images|displaytitle' + ( contentmodel !== 'wikitext' || fragment || disambiguation !== undefined ? '' : '&section=0' ) + '&disablelimitreport=true&disableeditsection=true&disabletoc=true&sectionpreview=true&page=' + encodeURIComponent( title ) + '&format=json', {
+			timeout: 10000
+		} ).then( response => {
+			if ( response.statusCode !== 200 || !response?.body?.parse?.text ) {
+				console.log( '- ' + response.statusCode + ': Error while parsing the page: ' + response?.body?.error?.info );
+				if ( embed.backupDescription && embed.length < 5000 ) {
+					embed.setDescription( embed.backupDescription );
+				}
+				if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+					embed.spliceFields( 0, 0, embed.backupField );
+				}
+				return;
 			}
-			if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
-				embed.spliceFields( 0, 0, embed.backupField );
+			var displaytitle = htmlToDiscord( response.body.parse.displaytitle );
+			if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
+			embed.setTitle( displaytitle );
+			var $ = cheerio.load(response.body.parse.text['*'].replace( /\n?<br(?: ?\/)?>\n?/g, '<br>' ));
+			if ( embed.brokenInfobox && $('aside.portable-infobox').length ) {
+				let infobox = $('aside.portable-infobox');
+				embed.fields.forEach( field => {
+					if ( embed.length > 5400 ) return;
+					if ( /^`.+`$/.test(field.name) ) {
+						let label = infobox.find(field.name.replace( /^`(.+)`$/, '[data-source="$1"] .pi-data-label, .pi-data-label[data-source="$1"]' )).html();
+						if ( !label ) label = infobox.find(field.name.replace( /^`(.+)`$/, '[data-item-name="$1"] .pi-data-label, .pi-data-label[data-item-name="$1"]' )).html();
+						if ( label ) {
+							label = htmlToPlain(label).trim();
+							if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
+							if ( label ) field.name = label;
+						}
+					}
+					if ( /^`.+`$/.test(field.value) ) {
+						let value = infobox.find(field.value.replace( /^`(.+)`$/, '[data-source="$1"] .pi-data-value, .pi-data-value[data-source="$1"]' )).html();
+						if ( !value ) value = infobox.find(field.value.replace( /^`(.+)`$/, '[data-item-name="$1"] .pi-data-value, .pi-data-value[data-item-name="$1"]' )).html();
+						if ( value ) {
+							value = htmlToDiscord(value, embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
+							if ( value.length > 500 ) value = limitLength(value, 500, 250);
+							if ( value ) field.value = value;
+						}
+					}
+				} );
 			}
-			return;
-		}
-		var displaytitle = htmlToDiscord( response.body.parse.displaytitle );
-		if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
-		embed.setTitle( displaytitle );
-		var $ = cheerio.load(response.body.parse.text['*'].replace( /\n?<br(?: ?\/)?>\n?/g, '<br>' ));
-		if ( embed.brokenInfobox && $('aside.portable-infobox').length ) {
-			let infobox = $('aside.portable-infobox');
-			embed.fields.forEach( field => {
-				if ( embed.length > 5400 ) return;
-				if ( /^`.+`$/.test(field.name) ) {
-					let label = infobox.find(field.name.replace( /^`(.+)`$/, '[data-source="$1"] .pi-data-label, .pi-data-label[data-source="$1"]' )).html();
-					if ( !label ) label = infobox.find(field.name.replace( /^`(.+)`$/, '[data-item-name="$1"] .pi-data-label, .pi-data-label[data-item-name="$1"]' )).html();
-					if ( label ) {
-						label = htmlToPlain(label).trim();
+			if ( !fragment && !embed.fields.length && $(infoboxList.join(', ')).length ) {
+				let infobox = $(infoboxList.join(', ')).first();
+				if ( embed.thumbnail?.url === thumbnail ) {
+					let image = infobox.find([
+						'tr:eq(1) img',
+						'div.images img',
+						'figure.pi-image img',
+						'div.infobox-imagearea img'
+					].join(', ')).toArray().find( img => {
+						let imgURL = img.attribs.src;
+						if ( !imgURL ) return false;
+						return ( /^(?:https?:)?\/\//.test(imgURL) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/i.test(imgURL) );
+					} )?.attribs.src?.replace( /^(?:https?:)?\/\//, 'https://' );
+					if ( image ) embed.setThumbnail( new URL(image, wiki).href );
+				}
+				let rows = infobox.find([
+					'> tbody > tr',
+					'> tbody > tr > th.mainheader',
+					'> table > tbody > tr',
+					'div.section > div.title',
+					'div.section > table > tbody > tr',
+					'h2.pi-header',
+					'div.pi-data',
+					'table.infobox-rows > tbody > tr',
+					'div.infobox-rows:not(.subinfobox) > div.infobox-row'
+				].join(', '));
+				let tdLabel = true;
+				for ( let i = 0; i < rows.length; i++ ) {
+					if ( embed.fields.length >= 25 || embed.length > 5400 ) break;
+					let row = rows.eq(i);
+					if ( row.is('th.mainheader, div.title, h2.pi-header') ) {
+						row.find(removeClasses.join(', ')).remove();
+						let label = htmlToPlain(row).trim();
 						if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
-						if ( label ) field.name = label;
+						if ( label ) {
+							if ( embed.fields.length && embed.fields[embed.fields.length - 1].name === '\u200b' ) {
+								embed.spliceFields( embed.fields.length - 1, 1, {
+									name: '\u200b',
+									value: '**' + label + '**',
+									inline: false
+								} );
+							}
+							else embed.addField( '\u200b', '**' + label + '**', false );
+						}
 					}
-				}
-				if ( /^`.+`$/.test(field.value) ) {
-					let value = infobox.find(field.value.replace( /^`(.+)`$/, '[data-source="$1"] .pi-data-value, .pi-data-value[data-source="$1"]' )).html();
-					if ( !value ) value = infobox.find(field.value.replace( /^`(.+)`$/, '[data-item-name="$1"] .pi-data-value, .pi-data-value[data-item-name="$1"]' )).html();
-					if ( value ) {
+					else if ( row.is('tr, div.pi-data, div.infobox-row') ) {
+						let label = row.children(( tdLabel ? 'td, ' : '' ) + 'th, h3.pi-data-label, div.infobox-cell-header').eq(0);
+						label.find(removeClasses.join(', ')).remove();
+						let value = row.children('td, div.pi-data-value, div.infobox-cell-data').eq(( label.is('td') ? 1 : 0 ));
+						value.find(removeClasses.join(', ')).remove();
+						if ( !label.is('td') && label.html()?.trim() && value.html()?.trim() ) tdLabel = false;
+						label = htmlToPlain(label).trim().split('\n')[0];
 						value = htmlToDiscord(value, embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
+						if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
 						if ( value.length > 500 ) value = limitLength(value, 500, 250);
-						if ( value ) field.value = value;
-					}
-				}
-			} );
-		}
-		if ( !fragment && !embed.fields.length && $(infoboxList.join(', ')).length ) {
-			let infobox = $(infoboxList.join(', ')).first();
-			if ( embed.thumbnail?.url === thumbnail ) {
-				let image = infobox.find([
-					'tr:eq(1) img',
-					'div.images img',
-					'figure.pi-image img',
-					'div.infobox-imagearea img'
-				].join(', ')).toArray().find( img => {
-					let imgURL = img.attribs.src;
-					if ( !imgURL ) return false;
-					return ( /^(?:https?:)?\/\//.test(imgURL) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/i.test(imgURL) );
-				} )?.attribs.src?.replace( /^(?:https?:)?\/\//, 'https://' );
-				if ( image ) embed.setThumbnail( new URL(image, wiki).href );
-			}
-			let rows = infobox.find([
-				'> tbody > tr',
-				'> tbody > tr > th.mainheader',
-				'> table > tbody > tr',
-				'div.section > div.title',
-				'div.section > table > tbody > tr',
-				'h2.pi-header',
-				'div.pi-data',
-				'table.infobox-rows > tbody > tr',
-				'div.infobox-rows:not(.subinfobox) > div.infobox-row'
-			].join(', '));
-			let tdLabel = true;
-			for ( let i = 0; i < rows.length; i++ ) {
-				if ( embed.fields.length >= 25 || embed.length > 5400 ) break;
-				let row = rows.eq(i);
-				if ( row.is('th.mainheader, div.title, h2.pi-header') ) {
-					row.find(removeClasses.join(', ')).remove();
-					let label = htmlToPlain(row).trim();
-					if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
-					if ( label ) {
-						if ( embed.fields.length && embed.fields[embed.fields.length - 1].name === '\u200b' ) {
-							embed.spliceFields( embed.fields.length - 1, 1, {
-								name: '\u200b',
-								value: '**' + label + '**',
-								inline: false
-							} );
-						}
-						else embed.addField( '\u200b', '**' + label + '**', false );
+						if ( label && value ) embed.addField( label, value, true );
 					}
 				}
-				else if ( row.is('tr, div.pi-data, div.infobox-row') ) {
-					let label = row.children(( tdLabel ? 'td, ' : '' ) + 'th, h3.pi-data-label, div.infobox-cell-header').eq(0);
-					label.find(removeClasses.join(', ')).remove();
-					let value = row.children('td, div.pi-data-value, div.infobox-cell-data').eq(( label.is('td') ? 1 : 0 ));
-					value.find(removeClasses.join(', ')).remove();
-					if ( !label.is('td') && label.html()?.trim() && value.html()?.trim() ) tdLabel = false;
-					label = htmlToPlain(label).trim().split('\n')[0];
-					value = htmlToDiscord(value, embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
-					if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
-					if ( value.length > 500 ) value = limitLength(value, 500, 250);
-					if ( label && value ) embed.addField( label, value, true );
+				if ( embed.fields.length && embed.fields[embed.fields.length - 1].name === '\u200b' ) {
+					embed.spliceFields( embed.fields.length - 1, 1 );
 				}
 			}
-			if ( embed.fields.length && embed.fields[embed.fields.length - 1].name === '\u200b' ) {
-				embed.spliceFields( embed.fields.length - 1, 1 );
+			if ( embed.thumbnail?.url === thumbnail ) {
+				let image = response.body.parse.images.find( pageimage => ( /\.(?:png|jpg|jpeg|gif)$/.test(pageimage.toLowerCase()) && pageimage.toLowerCase().includes( title.toLowerCase().replace( / /g, '_' ) ) ) );
+				if ( !image ) {
+					thumbnail = $(infoboxList.join(', ')).find('img').filter( (i, img) => {
+						img = $(img).prop('src')?.toLowerCase();
+						return ( /^(?:https?:)?\/\//.test(img) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/.test(img) );
+					} ).first().prop('src');
+					if ( !thumbnail ) thumbnail = $('img').filter( (i, img) => {
+						img = $(img).prop('src')?.toLowerCase();
+						return ( /^(?:https?:)?\/\//.test(img) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/.test(img) );
+					} ).first().prop('src');
+					if ( !thumbnail ) image = response.body.parse.images.find( pageimage => {
+						return /\.(?:png|jpg|jpeg|gif)$/.test(pageimage.toLowerCase());
+					} );
+				}
+				if ( image ) thumbnail = wiki.toLink('Special:FilePath/' + image);
+				if ( thumbnail ) embed.setThumbnail( thumbnail.replace( /^(?:https?:)?\/\//, 'https://' ) );
 			}
-		}
-		if ( embed.thumbnail?.url === thumbnail ) {
-			let image = response.body.parse.images.find( pageimage => ( /\.(?:png|jpg|jpeg|gif)$/.test(pageimage.toLowerCase()) && pageimage.toLowerCase().includes( title.toLowerCase().replace( / /g, '_' ) ) ) );
-			if ( !image ) {
-				thumbnail = $(infoboxList.join(', ')).find('img').filter( (i, img) => {
-					img = $(img).prop('src')?.toLowerCase();
-					return ( /^(?:https?:)?\/\//.test(img) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/.test(img) );
-				} ).first().prop('src');
-				if ( !thumbnail ) thumbnail = $('img').filter( (i, img) => {
-					img = $(img).prop('src')?.toLowerCase();
-					return ( /^(?:https?:)?\/\//.test(img) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/.test(img) );
-				} ).first().prop('src');
-				if ( !thumbnail ) image = response.body.parse.images.find( pageimage => {
-					return /\.(?:png|jpg|jpeg|gif)$/.test(pageimage.toLowerCase());
+			if ( fragment && embed.length < 4750 && embed.fields.length < 25 &&
+			toSection(embed.fields[0]?.name.replace( /^\**_*(.*?)_*\**$/g, '$1' )) !== toSection(fragment) ) {
+				let newFragment = '';
+				let exactMatch = true;
+				let allSections = $('h1, h2, h3, h4, h5, h6').children('span');
+				var section = allSections.filter( (i, span) => {
+					return ( '#' + span.attribs.id === toSection(fragment) );
+				} ).parent();
+				if ( !section.length ) {
+					section = $('[id="' + toSection(fragment, false).replace( '#', '' ) + '"]');
+					newFragment = section.attr('id');
+					if ( section.is(':empty') ) section = section.parent();
+				}
+				if ( !section.length ) exactMatch = false;
+				if ( !section.length ) section = allSections.filter( (i, span) => {
+					return ( '#' + span.attribs.id.toLowerCase() === toSection(fragment).toLowerCase() );
 				} );
-			}
-			if ( image ) thumbnail = wiki.toLink('Special:FilePath/' + image);
-			if ( thumbnail ) embed.setThumbnail( thumbnail.replace( /^(?:https?:)?\/\//, 'https://' ) );
-		}
-		if ( fragment && embed.length < 4750 && embed.fields.length < 25 &&
-		toSection(embed.fields[0]?.name.replace( /^\**_*(.*?)_*\**$/g, '$1' )) !== toSection(fragment) ) {
-			var section = $('h1, h2, h3, h4, h5, h6').children('span').filter( (i, span) => {
-				return ( '#' + span.attribs.id === toSection(fragment) );
-			} ).parent();
-			if ( section.length ) {
-				var sectionLevel = section[0].tagName.replace('h', '');
-				var sectionContent = $('<div>').append(
-					section.nextUntil(['h1','h2','h3','h4','h5','h6'].slice(0, sectionLevel).join(', '))
-				);
-				section.find('div, ' + removeClasses.join(', ')).remove();
-				sectionContent.find(infoboxList.join(', ')).remove();
-				sectionContent.find('div, ' + removeClasses.join(', ')).remove();
-				var name = htmlToPlain(section).trim();
-				if ( name.length > 250 ) name = name.substring(0, 250) + '\u2026';
-				var value = htmlToDiscord(sectionContent, embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
-				if ( value.length > 1000 ) value = limitLength(value, 1000, 20);
-				if ( name.length && value.length ) {
-					embed.spliceFields( 0, 0, {name, value} );
+				if ( !section.length ) section = allSections.filter( (i, span) => {
+					return ( $(span).parent().text().trim() === fragment );
+				} );
+				if ( !section.length ) section = allSections.filter( (i, span) => {
+					return ( $(span).parent().text().trim().toLowerCase() === fragment.toLowerCase() );
+				} );
+				if ( !section.length ) section = allSections.filter( (i, span) => {
+					return $(span).parent().text().toLowerCase().includes( fragment.toLowerCase() );
+				} );
+				if ( !exactMatch && section.length ) {
+					newFragment = section.attr('id');
+					section = section.parent();
+				}
+				if ( section.length ) {
+					section = section.first();
+					var sectionLevel = section[0].tagName.replace('h', '');
+					if ( !['1','2','3','4','5','6'].includes( sectionLevel ) ) sectionLevel = '10';
+					var sectionContent = $('<div>').append(
+						section.nextUntil(['h1','h2','h3','h4','h5','h6'].slice(0, sectionLevel).join(', '))
+					);
+					section.find('div, ' + removeClasses.join(', ')).remove();
+					sectionContent.find(infoboxList.join(', ')).remove();
+					sectionContent.find('div, ' + removeClasses.join(', ')).not(removeClassesExceptions.join(', ')).remove();
+					var name = htmlToPlain(section).trim();
+					if ( !name.length ) name = fragment.escapeFormatting();
+					if ( name.length > 250 ) name = name.substring(0, 250) + '\u2026';
+					var value = htmlToDiscord(sectionContent, embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
+					if ( value.length > 1000 ) value = limitLength(value, 1000, 20);
+					if ( name.length && value.length ) {
+						embed.spliceFields( 0, 0, {name, value} );
+						if ( newFragment ) {
+							embed.setURL( pagelink.replace( toSection(fragment), '#' + newFragment ) );
+							content = content.replace( '<' + pagelink + '>', '<' + embed.url + '>' );
+						}
+					}
+					else if ( embed.backupField ) {
+						embed.spliceFields( 0, 0, embed.backupField );
+					}
 				}
 				else if ( embed.backupField ) {
 					embed.spliceFields( 0, 0, embed.backupField );
 				}
 			}
-			else if ( embed.backupField ) {
-				embed.spliceFields( 0, 0, embed.backupField );
-			}
-		}
-		if ( !embed.description && embed.length < 5000 ) {
-			if ( contentmodel !== 'wikitext' || disambiguation === undefined || fragment ) {
-				$('h1, h2, h3, h4, h5, h6').nextAll().remove();
-				$('h1, h2, h3, h4, h5, h6').remove();
-			}
-			$(infoboxList.join(', ')).remove();
-			$('div, ' + removeClasses.join(', '), $('.mw-parser-output')).not(removeClassExceptions.join(', ')).remove();
-			var description = htmlToDiscord($.html(), embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
-			if ( description ) {
-				if ( disambiguation !== undefined && !fragment && embed.length < 4250 ) {
-					if ( description.length > 1500 ) description = limitLength(description, 1500, 250);
+			if ( !embed.description && embed.length < 5000 ) {
+				if ( contentmodel !== 'wikitext' || disambiguation === undefined || fragment ) {
+					$('h1, h2, h3, h4, h5, h6').nextAll().remove();
+					$('h1, h2, h3, h4, h5, h6').remove();
+				}
+				$(infoboxList.join(', ')).remove();
+				$('div, ' + removeClasses.join(', '), $('.mw-parser-output')).not(removeClassesExceptions.join(', ')).remove();
+				var description = htmlToDiscord($.html(), embed.url, true).trim().replace( /\n{3,}/g, '\n\n' );
+				if ( description ) {
+					if ( disambiguation !== undefined && !fragment && embed.length < 4250 ) {
+						if ( description.length > 1500 ) description = limitLength(description, 1500, 250);
+					}
+					else if ( description.length > 1000 ) description = limitLength(description, 1000, 500);
+					embed.setDescription( description );
+				}
+				else if ( embed.backupDescription ) {
+					embed.setDescription( embed.backupDescription );
 				}
-				else if ( description.length > 1000 ) description = limitLength(description, 1000, 500);
-				embed.setDescription( description );
 			}
-			else if ( embed.backupDescription ) {
+		}, error => {
+			console.log( '- Error while parsing the page: ' + error );
+			if ( embed.backupDescription && embed.length < 5000 ) {
 				embed.setDescription( embed.backupDescription );
 			}
-		}
+			if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+				embed.spliceFields( 0, 0, embed.backupField );
+			}
+		} ) }() )
+	]).then( ([message]) => {
+		if ( !message ) return;
+		message.edit( content, {embed,allowedMentions:{users:[msg.author.id]}} );
 	}, error => {
-		console.log( '- Error while parsing the page: ' + error );
-		if ( embed.backupDescription && embed.length < 5000 ) {
-			embed.setDescription( embed.backupDescription );
-		}
-		if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
-			embed.spliceFields( 0, 0, embed.backupField );
-		}
-	} ).finally( () => {
-		msg.sendChannel( content, {embed} );
-
-		if ( reaction ) reaction.removeEmoji();
+		console.log( '- Error while fetching the page description: ' + error );
 	} );
 }
 

+ 1 - 0
i18n/en.json

@@ -567,6 +567,7 @@
         "empty": "*This special page is empty*",
         "infopage": "Not the correct result? Use $1 for a direct link.",
         "infosearch": "Not the correct result? Use $1 for a direct link or $2 for a list of all hits.",
+        "loading": "Loading page description…",
         "media": "To the file description page",
         "results": "$1 total {{PLURAL:$2|result|results}}",
         "special": "Content of this special page:"

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 4214 - 1
package-lock.json


+ 1 - 1
package.json

@@ -22,7 +22,7 @@
     "full-icu": "^1.3.1",
     "got": "^11.8.1",
     "htmlparser2": "^6.0.0",
-    "npm": "^7.3.0",
+    "npm": "^7.5.3",
     "sqlite3": "^5.0.0"
   },
   "repository": {

+ 3 - 2
util/wiki.js

@@ -178,13 +178,14 @@ class Wiki extends URL {
 	/**
 	 * Encode a link section.
 	 * @param {String} [fragment] - Fragment of the page.
+	 * @param {Boolean} [simpleEncoding] - Don't fully encode the anchor.
 	 * @returns {String}
 	 * @static
 	 */
-	static toSection(fragment = '') {
+	static toSection(fragment = '', simpleEncoding = true) {
 		if ( !fragment ) return '';
 		fragment = fragment.replace( / /g, '_' );
-		if ( !/['"`^{}<>|\\]|@(everyone|here)/.test(fragment) ) return '#' + fragment;
+		if ( simpleEncoding && !/['"`^{}<>|\\]|@(everyone|here)/.test(fragment) ) return '#' + fragment;
 		return '#' + encodeURIComponent( fragment ).replace( /[!'()*~]/g, (match) => {
 			return '%' + match.charCodeAt().toString(16).toUpperCase();
 		} ).replace( /%3A/g, ':' ).replace( /%/g, '.' );

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov