Browse Source

add links to embed description

Markus-Rost 4 years ago
parent
commit
94f42a7733
5 changed files with 171 additions and 73 deletions
  1. 18 12
      cmds/wiki/general.js
  2. 3 3
      cmds/wiki/random.js
  3. 12 8
      cmds/wiki/user.js
  4. 46 12
      functions/parse_page.js
  5. 92 38
      util/functions.js

+ 18 - 12
cmds/wiki/general.js

@@ -204,13 +204,15 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 							}
 							if ( querypage.extract ) {
 								var extract = extract_desc(querypage.extract, fragment);
-								embed.setDescription( extract[0] );
-								if ( extract[2].length ) embed.addField( extract[1], extract[2] );
+								embed.backupDescription = extract[0];
+								if ( extract[1].length && extract[2].length ) {
+									embed.backupField = {name: extract[1], value: extract[2]};
+								}
 							}
 							if ( querypage.pageprops && querypage.pageprops.description ) {
 								var description = htmlToPlain( querypage.pageprops.description );
 								if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-								embed.setDescription( description );
+								embed.backupDescription = description;
 							}
 							if ( querypage.ns === 6 ) {
 								var pageimage = ( querypage?.original?.source || wiki.toLink('Special:FilePath/' + querypage.title, {version:Date.now()}) );
@@ -261,7 +263,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 							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);
+									parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, wiki.articleURL.href);
 								}
 								catch ( error ) {
 									console.log( '- Failed to parse the infobox: ' + error );
@@ -307,13 +309,15 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				}
 				if ( querypage.extract ) {
 					var extract = extract_desc(querypage.extract, ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ));
-					embed.setDescription( extract[0] );
-					if ( extract[2].length ) embed.addField( extract[1], extract[2] );
+					embed.backupDescription = extract[0];
+					if ( extract[1].length && extract[2].length ) {
+						embed.backupField = {name: extract[1], value: extract[2]};
+					}
 				}
 				if ( querypage.pageprops && querypage.pageprops.description ) {
 					var description = htmlToPlain( querypage.pageprops.description );
 					if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-					embed.setDescription( description );
+					embed.backupDescription = description;
 				}
 				if ( querypage.ns === 6 ) {
 					var pageimage = ( querypage?.original?.source || wiki.toLink('Special:FilePath/' + querypage.title, {version:Date.now()}) );
@@ -351,7 +355,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				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);
+						parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, wiki.articleURL.href);
 					}
 					catch ( error ) {
 						console.log( '- Failed to parse the infobox: ' + error );
@@ -424,20 +428,22 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 					}
 					if ( querypage.extract ) {
 						var extract = extract_desc(querypage.extract, fragment);
-						embed.setDescription( extract[0] );
-						if ( extract[2].length ) embed.addField( extract[1], extract[2] );
+						embed.backupDescription = extract[0];
+						if ( extract[1].length && extract[2].length ) {
+							embed.backupField = {name: extract[1], value: extract[2]};
+						}
 					}
 					if ( querypage.pageprops && querypage.pageprops.description ) {
 						var description = htmlToPlain( querypage.pageprops.description );
 						if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-						embed.setDescription( description );
+						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, '');
+						parse_infobox(infobox, embed, '', wiki.articleURL.href);
 					}
 					catch ( error ) {
 						console.log( '- Failed to parse the infobox: ' + error );

+ 3 - 3
cmds/wiki/random.js

@@ -41,11 +41,11 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler) {
 			if ( querypage.pageprops && querypage.pageprops.description ) {
 				var description = htmlToPlain( querypage.pageprops.description );
 				if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-				embed.setDescription( description );
+				embed.backupDescription = description;
 			}
 			else if ( querypage.extract ) {
 				var extract = extract_desc(querypage.extract);
-				embed.setDescription( extract[0] );
+				embed.backupDescription = extract[0];
 			}
 			if ( querypage.title === body.query.general.mainpage ) {
 				embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
@@ -61,7 +61,7 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler) {
 			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);
+					parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, wiki.articleURL.href);
 				}
 				catch ( error ) {
 					console.log( '- Failed to parse the infobox: ' + error );

+ 12 - 8
cmds/wiki/user.js

@@ -46,13 +46,15 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 					}
 					if ( querypage.extract ) {
 						var extract = extract_desc(querypage.extract, fragment);
-						embed.setDescription( extract[0] );
-						if ( extract[2].length ) embed.addField( extract[1], extract[2] );
+						embed.backupDescription = extract[0];
+						if ( extract[1].length && extract[2].length ) {
+							embed.backupField = {name: extract[1], value: extract[2]};
+						}
 					}
 					if ( querypage.pageprops && querypage.pageprops.description ) {
 						var description = htmlToPlain( querypage.pageprops.description );
 						if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-						embed.setDescription( description );
+						embed.backupDescription = description;
 					}
 					if ( querypage.pageimage && querypage.original ) {
 						embed.setThumbnail( querypage.original.source );
@@ -64,7 +66,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 					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);
+							parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, wiki.articleURL.href);
 						}
 						catch ( error ) {
 							console.log( '- Failed to parse the infobox: ' + error );
@@ -210,13 +212,15 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				}
 				if ( querypage.extract ) {
 					var extract = extract_desc(querypage.extract, fragment);
-					embed.setDescription( extract[0] );
-					if ( extract[2].length ) embed.addField( extract[1], extract[2] );
+					embed.backupDescription = extract[0];
+					if ( extract[1].length && extract[2].length ) {
+						embed.backupField = {name: extract[1], value: extract[2]};
+					}
 				}
 				if ( querypage.pageprops && querypage.pageprops.description ) {
 					var description = htmlToPlain( querypage.pageprops.description );
 					if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-					embed.setDescription( description );
+					embed.backupDescription = description;
 				}
 				if ( querypage.pageimage && querypage.original ) {
 					embed.setThumbnail( querypage.original.source );
@@ -229,7 +233,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				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);
+						parse_infobox(infobox, embed, new URL(body.query.general.logo, wiki).href, wiki.articleURL.href);
 					}
 					catch ( error ) {
 						console.log( '- Failed to parse the infobox: ' + error );

+ 46 - 12
functions/parse_page.js

@@ -1,6 +1,6 @@
 const cheerio = require('cheerio');
 const {toSection} = require('../util/wiki.js');
-const {htmlToPlain} = require('../util/functions.js');
+const {htmlToPlain, htmlToDiscord} = require('../util/functions.js');
 
 const infoboxList = [
 	'.infobox',
@@ -46,12 +46,20 @@ function parse_page(msg, title, embed, wiki, thumbnail, fragment = '') {
 	if ( !msg || ( embed.description && embed.thumbnail?.url !== thumbnail && !embed.brokenInfobox && !fragment ) ) {
 		return;
 	}
+	var change = false;
 	got.get( wiki + 'api.php?action=parse&prop=text|images' + ( fragment ? '' : '&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 );
+				change = true;
+			}
+			if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+				embed.spliceFields( 0, 0, embed.backupField );
+				change = true;
+			}
 			return;
 		}
-		var change = false;
 		var $ = cheerio.load(response.body.parse.text['*'].replace( /<br\/?>/g, '\n' ));
 		if ( embed.brokenInfobox && $('aside.portable-infobox').length ) {
 			var infobox = $('aside.portable-infobox');
@@ -59,15 +67,21 @@ function parse_page(msg, title, embed, wiki, thumbnail, fragment = '') {
 				if ( embed.length > 5500 ) 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();
-					label = htmlToPlain(label).trim();
-					if ( label.length > 50 ) label = label.substring(0, 50) + '\u2026';
-					if ( label ) field.name = label;
+					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 > 50 ) label = label.substring(0, 50) + '\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();
-					value = htmlToPlain(value).trim();
-					if ( value.length > 250 ) value = value.substring(0, 250) + '\u2026';
-					if ( value ) field.value = value;
+					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, wiki.articleURL.href, true).trim();
+						if ( value.length > 250 ) value = value.substring(0, 250) + '\u2026';
+						if ( value ) field.value = value;
+					}
 				}
 			} );
 			change = true;
@@ -108,12 +122,20 @@ function parse_page(msg, title, embed, wiki, thumbnail, fragment = '') {
 				sectionContent.find(removeClasses.join(', ')).remove();
 				var name = htmlToPlain(section).trim();
 				if ( name.length > 250 ) name = name.substring(0, 250) + '\u2026';
-				var value = htmlToPlain(sectionContent).trim();
+				var value = htmlToDiscord(sectionContent, wiki.articleURL.href, true).trim();
 				if ( value.length > 1000 ) value = value.substring(0, 1000) + '\u2026';
 				if ( name.length && value.length ) {
 					embed.spliceFields( 0, 0, {name, value} );
 					change = true;
 				}
+				else if ( embed.backupField ) {
+					embed.spliceFields( 0, 0, embed.backupField );
+					change = true;
+				}
+			}
+			else if ( embed.backupField ) {
+				embed.spliceFields( 0, 0, embed.backupField );
+				change = true;
 			}
 		}
 		if ( !embed.description && embed.length < 5000 ) {
@@ -121,17 +143,29 @@ function parse_page(msg, title, embed, wiki, thumbnail, fragment = '') {
 			$('h1, h2, h3, h4, h5, h6').remove();
 			$(infoboxList.join(', ')).remove();
 			$(removeClasses.join(', '), $('.mw-parser-output')).not(keepMainPageTag.join(', ')).remove();
-			var description = $.text().trim().replace( /\n{3,}/g, '\n\n' ).escapeFormatting();
+			var description = htmlToDiscord($.html(), wiki.articleURL.href, true).trim();
 			if ( description ) {
 				if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
 				embed.setDescription( description );
 				change = true;
 			}
+			else if ( embed.backupDescription ) {
+				embed.setDescription( embed.backupDescription );
+				change = true;
+			}
 		}
-		
-		if ( change ) msg.edit( msg.content, {embed,allowedMentions:{parse:[]}} ).catch(log_error);
 	}, error => {
 		console.log( '- Error while parsing the page: ' + error );
+		if ( embed.backupDescription && embed.length < 5000 ) {
+			embed.setDescription( embed.backupDescription );
+			change = true;
+		}
+		if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+			embed.spliceFields( 0, 0, embed.backupField );
+			change = true;
+		}
+	} ).finally( () => {
+		if ( change ) msg.edit( msg.content, {embed,allowedMentions:{parse:[]}} ).catch(log_error);
 	} );
 }
 

+ 92 - 38
util/functions.js

@@ -13,40 +13,61 @@ const got = require('got').extend( {
  * @param {Object} infobox - The content of the infobox.
  * @param {import('discord.js').MessageEmbed} embed - The message embed.
  * @param {String} [thumbnail] - The default thumbnail for the wiki.
+ * @param {String} [serverpath] - The article path for relative links.
  * @returns {import('discord.js').MessageEmbed?}
  */
-function parse_infobox(infobox, embed, thumbnail) {
+function parse_infobox(infobox, embed, thumbnail, serverpath = '') {
 	if ( !infobox || embed.fields.length >= 25 || embed.length > 5500 ) return;
 	if ( infobox.parser_tag_version === 2 ) {
 		infobox.data.forEach( group => {
-			parse_infobox(group, embed, thumbnail);
+			parse_infobox(group, embed, thumbnail, serverpath);
 		} );
 		embed.fields = embed.fields.filter( (field, i, fields) => {
-			if ( field.name !== '\u200b' ) return true;
-			return ( fields[i + 1]?.name && fields[i + 1].name !== '\u200b' );
+			if ( field.name !== '\u200b' || !field.value.startsWith( '__**' ) ) return true;
+			return ( fields[i + 1]?.name && ( fields[i + 1].name !== '\u200b' || !fields[i + 1].value.startsWith( '__**' ) ) );
 		} );
 		return embed;
 	}
 	switch ( infobox.type ) {
 		case 'data':
-			var {label = '', value = '', source = ''} = infobox.data;
+			var {label = '', value = '', source = '', 'item-name': name = ''} = infobox.data;
 			label = htmlToPlain(label).trim();
-			value = htmlToPlain(value).trim();
+			value = htmlToDiscord(value, serverpath, true).trim();
 			if ( label.includes( '*UNKNOWN LINK*' ) ) {
-				label = '`' + source + '`';
+				label = '`' + ( source || name )  + '`';
 				embed.brokenInfobox = true;
 			}
 			if ( value.includes( '*UNKNOWN LINK*' ) ) {
-				value = '`' + source + '`';
+				value = '`' + ( source || name ) + '`';
 				embed.brokenInfobox = true;
 			}
 			if ( label.length > 50 ) label = label.substring(0, 50) + '\u2026';
 			if ( value.length > 250 ) value = value.substring(0, 250) + '\u2026';
 			if ( label && value ) embed.addField( label, value, true );
 			break;
+		case 'panel':
+			var embedLength = embed.fields.length;
+			infobox.data.value.forEach( group => {
+				parse_infobox(group, embed, thumbnail, serverpath);
+			} );
+			embed.fields = embed.fields.filter( (field, i, fields) => {
+				if ( i < embedLength || field.name !== '\u200b' ) return true;
+				if ( !field.value.startsWith( '__**' ) ) return true;
+				return ( fields[i + 1]?.name && fields[i + 1].name !== '\u200b' );
+			} ).filter( (field, i, fields) => {
+				if ( i < embedLength || field.name !== '\u200b' ) return true;
+				if ( field.value.startsWith( '__**' ) ) return true;
+				return ( fields[i + 1]?.name && ( fields[i + 1].name !== '\u200b' || !fields[i + 1].value.startsWith( '__**' ) ) );
+			} );
+			break;
+		case 'section':
+			var {label = ''} = infobox.data;
+			label = htmlToPlain(label).trim();
+			if ( label.length > 100 ) label = label.substring(0, 100) + '\u2026';
+			if ( label ) embed.addField( '\u200b', '**' + label + '**', false );
 		case 'group':
 			infobox.data.value.forEach( group => {
-				parse_infobox(group, embed, thumbnail);
+				parse_infobox(group, embed, thumbnail, serverpath);
 			} );
 			break;
 		case 'header':
@@ -107,7 +128,7 @@ function toMarkdown(text = '', wiki, title = '', fullWikitext = false) {
 		while ( ( link = regex.exec(text) ) !== null ) {
 			text = text.replaceSave( link[0], '[' + link[2] + '](https://' + link[1] + ')' );
 		}
-		return htmlToDiscord( text, true, true ).replaceSave( /'''/g, '**' ).replaceSave( /''/g, '*' );
+		return htmlToDiscord( text, '', true, true ).replaceSave( /'''/g, '**' ).replaceSave( /''/g, '*' );
 	}
 	return escapeFormatting(text, true);
 };
@@ -187,45 +208,78 @@ function htmlToPlain(html) {
 /**
  * Change HTML text to markdown text.
  * @param {String} html - The text in HTML.
+ * @param {String} [serverpath] - The article path for relative links.
  * @param {Boolean[]} [escapeArgs] - Arguments for the escaping of text formatting.
  * @returns {String}
  */
-function htmlToDiscord(html, ...escapeArgs) {
+function htmlToDiscord(html, serverpath = '', ...escapeArgs) {
 	var text = '';
+	var href = '';
+	var reference = false;
+	var listlevel = -1;
 	var parser = new htmlparser.Parser( {
 		onopentag: (tagname, attribs) => {
-			switch (tagname) {
-				case 'b':
-					text += '**';
-					break;
-				case 'i':
-					text += '*';
-					break;
-				case 's':
-					text += '~~';
-					break;
-				case 'u':
-					text += '__';
-					break;
+			if ( tagname === 'b' ) text += '**';
+			if ( tagname === 'i' ) text += '*';
+			if ( tagname === 's' ) text += '~~';
+			if ( tagname === 'u' ) text += '__';
+			if ( !serverpath ) return;
+			if ( tagname === 'sup' && attribs.class === 'reference' ) reference = true;
+			if ( tagname === 'br' ) {
+				text += '\n';
+				if ( listlevel > -1 ) text += '\u200b '.repeat(4 * listlevel + 3);
+			}
+			if ( tagname === 'hr' ) {
+				if ( !text.endsWith( '\n' ) ) text += '\n';
+				text += '─'.repeat(10) + '\n';
+			}
+			if ( tagname === 'p' && !text.endsWith( '\n' ) ) text += '\n';
+			if ( tagname === 'ul' ) listlevel++;
+			if ( tagname === 'li' ) {
+				if ( !text.endsWith( '\n' ) ) text += '\n';
+				if ( listlevel > -1 ) text += '\u200b '.repeat(4 * listlevel);
+				text += '• ';
+			}
+			if ( tagname === 'h1' ) text += '***__';
+			if ( tagname === 'h2' ) text += '**__';
+			if ( tagname === 'h3' ) text += '**';
+			if ( tagname === 'h4' ) text += '__';
+			if ( tagname === 'h5' ) text += '*';
+			if ( tagname === 'h6' ) text += '';
+			if ( tagname === 'a' && attribs.href && attribs.class !== 'new' && /^(?:(?:https?:)?\/)?\//.test(attribs.href) ) {
+				href = new URL(attribs.href, serverpath).href;
+				text += '[';
 			}
 		},
 		ontext: (htmltext) => {
-			text += escapeFormatting(htmltext, ...escapeArgs);
+			if ( !reference ) {
+				if ( href ) htmltext = htmltext.replace( /[\[\]]/g, '\\$&' );
+				text += escapeFormatting(htmltext, ...escapeArgs);
+			}
 		},
 		onclosetag: (tagname) => {
-			switch (tagname) {
-				case 'b':
-					text += '**';
-					break;
-				case 'i':
-					text += '*';
-					break;
-				case 's':
-					text += '~~';
-					break;
-				case 'u':
-					text += '__';
-					break;
+			if ( tagname === 'b' ) text += '**';
+			if ( tagname === 'i' ) text += '*';
+			if ( tagname === 's' ) text += '~~';
+			if ( tagname === 'u' ) text += '__';
+			if ( !serverpath ) return;
+			if ( tagname === 'sup' ) reference = false;
+			if ( tagname === 'ul' ) listlevel--;
+			if ( tagname === 'h1' ) text += '__***';
+			if ( tagname === 'h2' ) text += '__**';
+			if ( tagname === 'h3' ) text += '**';
+			if ( tagname === 'h4' ) text += '__';
+			if ( tagname === 'h5' ) text += '*';
+			if ( tagname === 'h6' ) text += '';
+			if ( tagname === 'a' && href ) {
+				if ( text.endsWith( '[' ) ) text = text.substring(0, text.length - 1);
+				else text += '](' + href.replace( /[()]/g, '\\$&' ) + ')';
+				href = '';
+			}
+		},
+		oncomment: (commenttext) => {
+			if ( serverpath && /^LINK'" \d+:\d+$/.test(commenttext) ) {
+				text += '*UNKNOWN LINK*';
 			}
 		}
 	} );