Markus-Rost 4 gadi atpakaļ
vecāks
revīzija
c5bd1b2acd

+ 1 - 2
bot.js

@@ -19,6 +19,7 @@ global.got = require('got').extend( {
 
 const {defaultSettings} = require('./util/default.json');
 const Lang = require('./util/i18n.js');
+const Wiki = require('./util/wiki.js');
 const newMessage = require('./util/newMessage.js');
 global.patreons = {};
 global.voice = {};
@@ -205,8 +206,6 @@ String.prototype.hasPrefix = function(prefix, flags = '') {
 	return regex.test(this.replace( /\u200b/g, '' ).toLowerCase());
 };
 
-const Wiki = require('./util/wiki.js');
-const slash_inline = require('./interactions/inline.js');
 const fs = require('fs');
 var slash = {};
 fs.readdir( './interactions', (error, files) => {

+ 56 - 22
cmds/rcscript.js

@@ -56,19 +56,26 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 			if ( rows.length >= limit ) return msg.replyMsg( lang.get('rcscript.max_entries'), {}, true );
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 
-			var wikiinvalid = lang.get('settings.wikiinvalid') + '\n`' + prefix + 'rcscript add ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
+			var wikihelp = '\n`' + prefix + 'rcscript add ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
 			var input = args.slice(1).join(' ').toLowerCase().trim().replace( /^<\s*(.*?)\s*>$/, '$1' );
 			var wikinew = new Wiki(wiki);
 			if ( input ) {
 				wikinew = Wiki.fromInput(input);
-				if ( !wikinew ) return msg.replyMsg( wikiinvalid, {}, true );
+				if ( !wikinew ) return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
 			}
-			return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' ).then( response => {
-				if ( response.statusCode === 404 && typeof response.body === 'string' ) {
-					let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
-					if ( api ) {
-						wikinew = new Wiki(api.split('api.php?')[0], wikinew);
-						return got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+			return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json', {
+				responseType: 'text'
+			} ).then( response => {
+				try {
+					response.body = JSON.parse(response.body);
+				}
+				catch (error) {
+					if ( response.statusCode === 404 && typeof response.body === 'string' ) {
+						let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
+						if ( api ) {
+							wikinew = new Wiki(api.split('api.php?')[0], wikinew);
+							return got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+						}
 					}
 				}
 				return response;
@@ -77,8 +84,11 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages || !body?.query?.general || !body?.query?.pages?.['-1'] ) {
 					console.log( '- ' + response.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
 					if ( reaction ) reaction.removeEmoji();
+					if ( body?.error?.info === 'You need read permission to use this module.' ) {
+						return msg.replyMsg( lang.get('settings.wikiinvalid_private') + wikihelp, {}, true );
+					}
 					msg.reactEmoji('nowiki', true);
-					return msg.replyMsg( wikiinvalid, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
 				}
 				wikinew.updateWiki(body.query.general);
 				if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) < 30 ) {
@@ -154,10 +164,17 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					}
 				} );
 			}, error => {
-				console.log( '- Error while testing the wiki: ' + error );
 				if ( reaction ) reaction.removeEmoji();
+				if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+					console.log( '- Error while testing the wiki: No HTTPS' );
+					return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {}, true );
+				}
+				console.log( '- Error while testing the wiki: ' + error );
+				if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+					return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {}, true );
+				}
 				msg.reactEmoji('nowiki', true);
-				return msg.replyMsg( wikiinvalid, {}, true );
+				return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
 			} ) );
 		}
 
@@ -217,15 +234,22 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				}
 				if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 
-				var wikiinvalid = lang.get('settings.wikiinvalid') + '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
+				var wikihelp = '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
 				var wikinew = Wiki.fromInput(args[1]);
-				if ( !wikinew ) return msg.replyMsg( wikiinvalid, {}, true );
-				return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' ).then( response => {
-					if ( response.statusCode === 404 && typeof response.body === 'string' ) {
-						let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
-						if ( api ) {
-							wikinew = new Wiki(api.split('api.php?')[0], wikinew);
-							return got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+				if ( !wikinew ) return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+				return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json', {
+					responseType: 'text'
+				} ).then( response => {
+					try {
+						response.body = JSON.parse(response.body);
+					}
+					catch (error) {
+						if ( response.statusCode === 404 && typeof response.body === 'string' ) {
+							let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
+							if ( api ) {
+								wikinew = new Wiki(api.split('api.php?')[0], wikinew);
+								return got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+							}
 						}
 					}
 					return response;
@@ -234,8 +258,11 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages || !body?.query?.general || !body?.query?.pages?.['-1'] ) {
 						console.log( '- ' + response.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
 						if ( reaction ) reaction.removeEmoji();
+						if ( body?.error?.info === 'You need read permission to use this module.' ) {
+							return msg.replyMsg( lang.get('settings.wikiinvalid_private') + wikihelp, {}, true );
+						}
 						msg.reactEmoji('nowiki', true);
-						return msg.replyMsg( wikiinvalid, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
 					}
 					wikinew.updateWiki(body.query.general);
 					if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) <= 30 ) {
@@ -298,10 +325,17 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 						}
 					} );
 				}, error => {
-					console.log( '- Error while testing the wiki: ' + error );
 					if ( reaction ) reaction.removeEmoji();
+					if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+						console.log( '- Error while testing the wiki: No HTTPS' );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {}, true );
+					}
+					console.log( '- Error while testing the wiki: ' + error );
+					if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+						return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {}, true );
+					}
 					msg.reactEmoji('nowiki', true);
-					return msg.replyMsg( wikiinvalid, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
 				} ) );
 			}
 			if ( args[0] === 'lang' ) {

+ 2 - 2
dashboard/guilds.js

@@ -103,7 +103,7 @@ function dashboard_guilds(res, dashboardLang, state, reqURL, action, actionArgs)
 		$('head title').text(`${guild.name} – ` + $('head title').text());
 		$('<script>').text(`
 			const isPatreon = ${guild.patreon};
-			const i18n = ${JSON.stringify(dashboardLang.get('indexjs'))};
+			const i18n = ${JSON.stringify(dashboardLang.getWithFallback('indexjs'))};
 		`).insertBefore('script#indexjs');
 		$('.channel#settings').attr('href', `/guild/${guild.id}/settings`);
 		$('.channel#verification').attr('href', `/guild/${guild.id}/verification`);
@@ -144,7 +144,7 @@ function dashboard_guilds(res, dashboardLang, state, reqURL, action, actionArgs)
 		$('head title').text(`${guild.name} – ` + $('head title').text());
 		$('<script>').text(`
 			const isPatreon = ${guild.patreon};
-			const i18n = ${JSON.stringify(dashboardLang.get('indexjs'))};
+			const i18n = ${JSON.stringify(dashboardLang.getWithFallback('indexjs'))};
 		`).insertBefore('script#indexjs');
 		$('.channel#settings').attr('href', `/guild/${guild.id}/settings?owner=true`);
 		$('.channel#verification').attr('href', `/guild/${guild.id}/verification?owner=true`);

+ 18 - 1
dashboard/i18n.js

@@ -37,7 +37,7 @@ class Lang {
 		for ( let n = 0; n < keys.length; n++ ) {
 			if ( text ) {
 				text = text?.[keys[n]];
-				if ( typeof text === 'string' ) text = text.trim()
+				if ( typeof text === 'string' ) text = text.trim();
 			}
 			if ( !text ) {
 				if ( fallback < this.fallback.length ) {
@@ -68,6 +68,23 @@ class Lang {
 		return ( text || '⧼' + message + ( isDebug && args.length ? ': ' + args.join(', ') : '' ) + '⧽' );
 	}
 
+	/**
+	 * Get a localized message with all fallback languages.
+	 * @param {String} message - Name of the message.
+	 * @returns {Object[]}
+	 */
+	getWithFallback(message = '') {
+		return [this.lang, ...this.fallback].map( lang => {
+			let keys = ( message.length ? message.split('.') : [] );
+			let text = i18n?.[lang];
+			for ( let n = 0; n < keys.length; n++ ) {
+				if ( text ) text = text?.[keys[n]];
+				if ( !text ) n = keys.length;
+			}
+			return text;
+		} );
+	}
+
 	/**
 	 * Get names for all languages.
 	 * @static

+ 6 - 0
dashboard/i18n/en.json

@@ -24,6 +24,9 @@
     },
     "indexjs": {
         "invalid": {
+            "note_http": "The provided website doesn't use HTTPS!",
+            "note_private": "The provided wiki is private!",
+            "note_timeout": "The provided link took too long to respond!",
             "text": "The URL couldn't be resolved to a valid MediaWiki site!",
             "title": "Invalid wiki!"
         },
@@ -103,6 +106,9 @@
             "title": "Settings saved!"
         },
         "savefail": {
+            "note_http": "The provided website doesn't use HTTPS!",
+            "note_private": "The provided wiki is private!",
+            "note_timeout": "The provided link took too long to respond!",
             "text": "The settings could not be saved, please try again.",
             "title": "Save failed!"
         },

+ 26 - 6
dashboard/oauth.js

@@ -228,6 +228,7 @@ function dashboard_api(res, input) {
 	var result = {
 		api: true,
 		error: false,
+		error_code: '',
 		wiki: wiki.href,
 		MediaWiki: false,
 		TextExtracts: false,
@@ -235,12 +236,19 @@ function dashboard_api(res, input) {
 		RcGcDw: '',
 		customRcGcDw: wiki.toLink('MediaWiki:Custom-RcGcDw', 'action=edit')
 	};
-	return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general|extensions&format=json' ).then( response => {
-		if ( response.statusCode === 404 && typeof response.body === 'string' ) {
-			let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
-			if ( api ) {
-				wiki = new Wiki(api.split('api.php?')[0], wiki);
-				return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general|extensions&format=json' );
+	return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general|extensions&format=json', {
+		responseType: 'text'
+	} ).then( response => {
+		try {
+			response.body = JSON.parse(response.body);
+		}
+		catch (error) {
+			if ( response.statusCode === 404 && typeof response.body === 'string' ) {
+				let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
+				if ( api ) {
+					wiki = new Wiki(api.split('api.php?')[0], wiki);
+					return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general|extensions&format=json' );
+				}
 			}
 		}
 		return response;
@@ -248,6 +256,9 @@ function dashboard_api(res, input) {
 		var body = response.body;
 		if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages || !body?.query?.general || !body?.query?.extensions ) {
 			console.log( '- Dashboard: ' + response.statusCode + ': Error while checking the wiki: ' + body?.error?.info );
+			if ( body?.error?.info === 'You need read permission to use this module.' ) {
+				result.error_code = 'private';
+			}
 			result.error = true;
 			return;
 		}
@@ -268,7 +279,16 @@ function dashboard_api(res, input) {
 		result.customRcGcDw = wiki.toLink('MediaWiki:Custom-RcGcDw', 'action=edit');
 		if ( wiki.isFandom() ) return;
 	}, error => {
+		if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+			console.log( '- Dashboard: Error while testing the wiki: No HTTPS' );
+			result.error_code = 'http';
+			result.error = true;
+			return;
+		}
 		console.log( '- Dashboard: Error while checking the wiki: ' + error );
+		if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+			result.error_code = 'timeout';
+		}
 		result.error = true;
 	} ).finally( () => {
 		let body = JSON.stringify(result);

+ 46 - 12
dashboard/rcscript.js

@@ -331,12 +331,19 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 				return res(`/guild/${guild}/rcscript`, 'savefail');
 			}
 			var wiki = Wiki.fromInput(settings.wiki);
-			return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' ).then( fresponse => {
-				if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
-					let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
-					if ( api ) {
-						wiki = new Wiki(api.split('api.php?')[0], wiki);
-						return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+			return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json', {
+				responseType: 'text'
+			} ).then( fresponse => {
+				try {
+					fresponse.body = JSON.parse(fresponse.body);
+				}
+				catch (error) {
+					if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
+						let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
+						if ( api ) {
+							wiki = new Wiki(api.split('api.php?')[0], wiki);
+							return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general&titles=Special:RecentChanges&format=json' );
+						}
 					}
 				}
 				return fresponse;
@@ -344,6 +351,9 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 				var body = fresponse.body;
 				if ( fresponse.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages || !body?.query?.general || !body?.query?.pages?.['-1'] ) {
 					console.log( '- Dashboard: ' + fresponse.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
+					if ( body?.error?.info === 'You need read permission to use this module.' ) {
+						return res(`/guild/${guild}/rcscript/new`, 'savefail', 'private');
+					}
 					return res(`/guild/${guild}/rcscript/new`, 'savefail');
 				}
 				wiki.updateWiki(body.query.general);
@@ -429,7 +439,14 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 					}
 				} );
 			}, error => {
+				if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+					console.log( '- Dashboard: Error while testing the wiki: No HTTPS' );
+					return res(`/guild/${guild}/rcscript/new`, 'savefail', 'http');
+				}
 				console.log( '- Dashboard: Error while testing the wiki: ' + error );
+				if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+					return res(`/guild/${guild}/rcscript/new`, 'savefail', 'timeout');
+				}
 				return res(`/guild/${guild}/rcscript/new`, 'savefail');
 			} );
 		} );
@@ -545,12 +562,19 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 				if ( ( row.postid === '-1' ) !== !settings.feeds ) hasDiff = true;
 				if ( !hasDiff ) return res(`/guild/${guild}/rcscript/${type}`, 'save');
 				var wiki = Wiki.fromInput(settings.wiki);
-				return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&format=json' ).then( fresponse => {
-					if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
-						let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
-						if ( api ) {
-							wiki = new Wiki(api.split('api.php?')[0], wiki);
-							return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&format=json' );
+				return got.get( wiki + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&format=json', {
+					responseType: 'text'
+				} ).then( fresponse => {
+					try {
+						fresponse.body = JSON.parse(fresponse.body);
+					}
+					catch (error) {
+						if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
+							let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
+							if ( api ) {
+								wiki = new Wiki(api.split('api.php?')[0], wiki);
+								return got.get( wiki + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general&format=json' );
+							}
 						}
 					}
 					return fresponse;
@@ -558,6 +582,9 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 					var body = fresponse.body;
 					if ( fresponse.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages || !body?.query?.general ) {
 						console.log( '- Dashboard: ' + fresponse.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
+						if ( body?.error?.info === 'You need read permission to use this module.' ) {
+							return res(`/guild/${guild}/rcscript/${type}`, 'savefail', 'private');
+						}
 						return res(`/guild/${guild}/rcscript/${type}`, 'savefail');
 					}
 					wiki.updateWiki(body.query.general);
@@ -732,7 +759,14 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 						}
 					} );
 				}, error => {
+					if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+						console.log( '- Dashboard: Error while testing the wiki: No HTTPS' );
+						return res(`/guild/${guild}/rcscript/${type}`, 'savefail', 'http');
+					}
 					console.log( '- Dashboard: Error while testing the wiki: ' + error );
+					if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+						return res(`/guild/${guild}/rcscript/${type}`, 'savefail', 'timeout');
+					}
 					return res(`/guild/${guild}/rcscript/${type}`, 'savefail');
 				} );
 			}, error => {

+ 25 - 8
dashboard/settings.js

@@ -354,12 +354,19 @@ function update_settings(res, userSettings, guild, type, settings) {
 			} );
 		} );
 		var wiki = Wiki.fromInput(settings.wiki);
-		return got.get( wiki + 'api.php?&action=query&meta=siteinfo&siprop=general|extensions&format=json' ).then( fresponse => {
-			if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
-				let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
-				if ( api ) {
-					wiki = new Wiki(api.split('api.php?')[0], wiki);
-					return got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general|extensions&format=json' );
+		return got.get( wiki + 'api.php?&action=query&meta=siteinfo&siprop=general|extensions&format=json', {
+			responseType: 'text'
+		} ).then( fresponse => {
+			try {
+				fresponse.body = JSON.parse(fresponse.body);
+			}
+			catch (error) {
+				if ( fresponse.statusCode === 404 && typeof fresponse.body === 'string' ) {
+					let api = cheerio.load(fresponse.body)('head link[rel="EditURI"]').prop('href');
+					if ( api ) {
+						wikinew = new Wiki(api.split('api.php?')[0], wikinew);
+						return got.get( wikinew + 'api.php?action=query&meta=siteinfo&siprop=general|extensions&format=json' );
+					}
 				}
 			}
 			return fresponse;
@@ -374,6 +381,9 @@ function update_settings(res, userSettings, guild, type, settings) {
 					if ( fresponse.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.general || !body?.query?.extensions ) {
 						console.log( '- Dashboard: ' + fresponse.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
 						if ( row?.wiki === wiki.href ) return resolve(row);
+						if ( body?.error?.info === 'You need read permission to use this module.' ) {
+							return reject('private');
+						}
 						return reject();
 					}
 					wiki.updateWiki(body.query.general);
@@ -381,7 +391,14 @@ function update_settings(res, userSettings, guild, type, settings) {
 				} );
 			} );
 		}, error => {
+			if ( error.message?.startsWith( 'connect ECONNREFUSED ' ) || error.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || error.message === 'certificate has expired' ) {
+				console.log( '- Dashboard: Error while testing the wiki: No HTTPS' );
+				return Promise.reject('http');
+			}
 			console.log( '- Dashboard: Error while testing the wiki: ' + error );
+			if ( error.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
+				return Promise.reject('timeout');
+			}
 			return Promise.reject();
 		} ).then( (row, query) => {
 			var lang = new Lang(( type === 'default' && settings.lang || row.guildlang ));
@@ -604,8 +621,8 @@ function update_settings(res, userSettings, guild, type, settings) {
 					} );
 				} );
 			} );
-		}, () => {
-			return res(`/guild/${guild}/settings/${type}`, 'savefail');
+		}, error => {
+			return res(`/guild/${guild}/settings/${type}`, 'savefail', error);
 		} );
 	}, error => {
 		console.log( '- Dashboard: Error while getting the member: ' + error );

+ 45 - 17
dashboard/src/index.js

@@ -1,3 +1,26 @@
+function lang(message = '') {
+	var keys = ( message.length ? message.split('.') : [] );
+	var text = i18n[0];
+	var fallback = 1;
+	for ( var n = 0; n < keys.length; n++ ) {
+		if ( text ) {
+			text = text[keys[n]];
+			if ( typeof text === 'string' ) text = text.trim();
+		}
+		if ( !text ) {
+			if ( fallback < i18n.length ) {
+				text = i18n[fallback];
+				fallback++;
+				n = -1;
+			}
+			else {
+				n = keys.length;
+			}
+		}
+	}
+	return ( text || '⧼' + message + '⧽' );
+}
+
 var baseSelect = document.getElementsByTagName('select');
 for ( var b = 0; b < baseSelect.length; b++ ) {
 	if ( baseSelect[b].id === 'wb-settings-lang' ) {
@@ -113,24 +136,29 @@ if ( wiki ) {
 				wikichecknotice.className = 'notice';
 				wikichecknotice.innerHTML = '';
 				if ( response.error ) {
-					wiki.setCustomValidity(i18n.invalid.title);
+					wiki.setCustomValidity(lang('invalid.title'));
 					wikichecknotice.classList.add('notice-error');
 					var noticeTitle = document.createElement('b');
-					noticeTitle.textContent = i18n.invalid.title;
+					noticeTitle.textContent = lang('invalid.title');
 					var noticeText = document.createElement('div');
-					noticeText.textContent = i18n.invalid.text;
-					wikichecknotice.append(noticeTitle, noticeText);
+					noticeText.textContent = lang('invalid.text');
+					var noticeNote = '';
+					if ( response.error_code ) {
+						noticeNote = document.createElement('div');
+						noticeNote.textContent = lang('invalid.note_' + response.error_code);
+					}
+					wikichecknotice.append(noticeTitle, noticeText, noticeNote);
 					return;
 				}
 				wiki.value = response.wiki;
 				if ( document.location.pathname.split('/')[3] === 'rcscript' ) {
 					if ( !response.MediaWiki ) {
-						wiki.setCustomValidity(i18n.outdated.title);
+						wiki.setCustomValidity(lang('outdated.title'));
 						wikichecknotice.classList.add('notice-error');
 						var noticeTitle = document.createElement('b');
-						noticeTitle.textContent = i18n.outdated.title;
+						noticeTitle.textContent = lang('outdated.title');
 						var noticeText = document.createElement('div');
-						noticeText.textContent = i18n.outdated.text;
+						noticeText.textContent = lang('outdated.text');
 						var noticeLink = document.createElement('a');
 						noticeLink.setAttribute('target', '_blank');
 						noticeLink.setAttribute('href', 'https://www.mediawiki.org/wiki/MediaWiki_1.30');
@@ -141,7 +169,7 @@ if ( wiki ) {
 					if ( response.RcGcDw !== document.location.pathname.split('/')[2] ) {
 						wikichecknotice.classList.add('notice-info');
 						var noticeTitle = document.createElement('b');
-						noticeTitle.textContent = i18n.sysmessage.title;
+						noticeTitle.textContent = lang('sysmessage.title');
 						var sysmessageLink = document.createElement('a');
 						sysmessageLink.setAttribute('target', '_blank');
 						sysmessageLink.setAttribute('href', response.customRcGcDw);
@@ -152,7 +180,7 @@ if ( wiki ) {
 						guildCode.className = 'user-select';
 						guildCode.textContent = document.location.pathname.split('/')[2];
 						var noticeText = document.createElement('div');
-						var textSnippets = i18n.sysmessage.text.split(/\$\d/);
+						var textSnippets = lang('sysmessage.text').split(/\$\d/);
 						noticeText.append(
 							document.createTextNode(textSnippets[0]),
 							sysmessageLink,
@@ -167,13 +195,13 @@ if ( wiki ) {
 					}
 					wikichecknotice.classList.add('notice-success');
 					var noticeTitle = document.createElement('b');
-					noticeTitle.textContent = i18n.valid.title;
+					noticeTitle.textContent = lang('valid.title');
 					wikichecknotice.append(noticeTitle);
 					return;
 				}
 				wikichecknotice.classList.add('notice-success');
 				var noticeTitle = document.createElement('b');
-				noticeTitle.textContent = i18n.valid.title;
+				noticeTitle.textContent = lang('valid.title');
 				wikichecknotice.append(noticeTitle);
 				if ( !/\.(?:gamepedia\.com|fandom\.com|wikia\.org)$/.test(wiki.value.split('/')[2]) ) {
 					if ( !response.MediaWiki ) {
@@ -182,7 +210,7 @@ if ( wiki ) {
 						noticeLink.setAttribute('href', 'https://www.mediawiki.org/wiki/MediaWiki_1.30');
 						noticeLink.textContent = 'MediaWiki 1.30';
 						var noticeText = document.createElement('div');
-						var textSnippets = i18n.valid.MediaWiki.split(/\$\d/);
+						var textSnippets = lang('valid.MediaWiki').split(/\$\d/);
 						noticeText.append(
 							document.createTextNode(textSnippets[0]),
 							noticeLink,
@@ -196,7 +224,7 @@ if ( wiki ) {
 						noticeLink.setAttribute('href', 'https://www.mediawiki.org/wiki/Extension:TextExtracts');
 						noticeLink.textContent = 'TextExtracts';
 						var noticeText = document.createElement('div');
-						var textSnippets = i18n.valid.TextExtracts.split(/\$\d/);
+						var textSnippets = lang('valid.TextExtracts').split(/\$\d/);
 						noticeText.append(
 							document.createTextNode(textSnippets[0]),
 							noticeLink,
@@ -210,7 +238,7 @@ if ( wiki ) {
 						noticeLink.setAttribute('href', 'https://www.mediawiki.org/wiki/Extension:PageImages');
 						noticeLink.textContent = 'PageImages';
 						var noticeText = document.createElement('div');
-						var textSnippets = i18n.valid.PageImages.split(/\$\d/);
+						var textSnippets = lang('valid.PageImages').split(/\$\d/);
 						noticeText.append(
 							document.createTextNode(textSnippets[0]),
 							noticeLink,
@@ -307,13 +335,13 @@ const prefix = document.getElementById('wb-settings-prefix');
 if ( prefix ) prefix.addEventListener( 'input', function() {
 	if ( prefix.validity.patternMismatch ) {
 		if ( prefix.value.trim().includes( ' ' ) ) {
-			prefix.setCustomValidity(i18n.prefix.space);
+			prefix.setCustomValidity(lang('prefix.space'));
 		}
 		else if ( prefix.value.includes( '`' ) ) {
-			prefix.setCustomValidity(i18n.prefix.code);
+			prefix.setCustomValidity(lang('prefix.code'));
 		}
 		else if ( prefix.value.includes( '\\' ) ) {
-			prefix.setCustomValidity(i18n.prefix.backslash);
+			prefix.setCustomValidity(lang('prefix.backslash'));
 		}
 		else prefix.setCustomValidity('');
 	}

+ 3 - 0
dashboard/util.js

@@ -192,6 +192,9 @@ function createNotice($, notice, dashboardLang, args = []) {
 			type = 'error';
 			title.text(dashboardLang.get('notice.savefail.title'));
 			text.text(dashboardLang.get('notice.savefail.text'));
+			if ( typeof args[0] === 'string' ) {
+				note = $('<div>').text(dashboardLang.get('notice.savefail.note_' + args[0]));
+			}
 			break;
 		case 'movefail':
 			type = 'info';

+ 12 - 16
functions/parse_page.js

@@ -88,19 +88,16 @@ const removeClassesExceptions = [
  * @param {String} [pagelink] - The link to the page.
  */
 function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmodel, pageprops: {infoboxes, disambiguation} = {}, uselang = lang.lang, noRedirect = false}, thumbnail, fragment = '', pagelink = '') {
+	if ( reaction ) reaction.removeEmoji();
 	if ( !msg?.showEmbed?.() ) {
 		msg.sendChannel( content, {embed} );
-
-		if ( reaction ) reaction.removeEmoji();
 		return;
 	}
-
-	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', {
+	msg.sendChannel( content, {
+		embed: new MessageEmbed(embed).setDescription( '<a:loading:641343250661113886> **' + lang.get('search.loading') + '**' )
+	} ).then( message => {
+		if ( !message ) 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', {
 			timeout: 10000
 		} ).then( response => {
 			var body = response.body;
@@ -149,7 +146,9 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 			if ( embed.backupDescription && embed.length < 5000 ) {
 				embed.setDescription( embed.backupDescription );
 			}
-		} ) : function() {
+		} ).finally( () => {
+			message.edit( content, {embed,allowedMentions:{users:[msg.author.id]}} ).catch(log_error);
+		} );
 		if ( !fragment && !embed.fields.length && infoboxes ) {
 			try {
 				var infobox = JSON.parse(infoboxes)?.[0];
@@ -367,12 +366,9 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 			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 fetching the page description: ' + error );
+		} ).finally( () => {
+			message.edit( content, {embed,allowedMentions:{users:[msg.author.id]}} ).catch(log_error);
+		} );
 	} );
 }
 

+ 1 - 1
util/i18n.js

@@ -57,7 +57,7 @@ class Lang {
 		for ( let n = 0; n < keys.length; n++ ) {
 			if ( text ) {
 				text = text?.[keys[n]];
-				if ( typeof text === 'string' ) text = text.trim()
+				if ( typeof text === 'string' ) text = text.trim();
 			}
 			if ( !text ) {
 				if ( fallback < this.fallback.length ) {