Browse Source

Add button linking to dashboard

Markus-Rost 4 years ago
parent
commit
41aa2cc41d
7 changed files with 224 additions and 131 deletions
  1. 7 0
      bot.js
  2. 80 55
      cmds/rcscript.js
  3. 55 34
      cmds/settings.js
  4. 53 30
      cmds/verification.js
  5. 2 2
      dashboard/i18n/en.json
  6. 26 10
      dashboard/src/index.js
  7. 1 0
      i18n/en.json

+ 7 - 0
bot.js

@@ -114,6 +114,13 @@ String.prototype.replaceSave = function(pattern, replacement) {
 	return this.replace( pattern, ( typeof replacement === 'string' ? replacement.replace( /\$/g, '$$$$' ) : replacement ) );
 };
 
+Discord.APIMessage.prototype._resolveDataOld = Discord.APIMessage.prototype.resolveData;
+Discord.APIMessage.prototype.resolveData = function() {
+	this._resolveDataOld();
+	if ( this.options.components ) this.data.components = this.options.components;
+	return this;
+};
+
 Discord.Message.prototype.reactEmoji = function(name, ignorePause = false) {
 	if ( !this.channel.isGuild() || !pause[this.guild.id] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
 		var emoji = ':error:440871715938238494';

+ 80 - 55
cmds/rcscript.js

@@ -7,7 +7,7 @@ const Wiki = require('../util/wiki.js');
 var db = require('../util/database.js');
 
 const fs = require('fs');
-const rcscriptExists = fs.existsSync('./RcGcDb/start.py');
+const rcscriptExists = ( isDebug || fs.existsSync('./RcGcDb/start.py') );
 
 const display_types = [
 	'compact',
@@ -38,6 +38,26 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 			limit = rcgcdwLimit.patreon;
 			display = display_types.slice();
 		}
+		var button = {
+			type: 2,
+			style: 5,
+			label: lang.get('settings.button'),
+			emoji: {
+				id: '588723748757307403',
+				name: 'wikibot',
+				animated: false
+			},
+			url: new URL(`/guild/${msg.guild.id}/rcscript`, process.env.dashboard).href,
+			disabled: false
+		};
+		var components = [
+			{
+				type: 1,
+				components: [
+					button
+				]
+			}
+		];
 
 		if ( args[0] === 'add' ) {
 			if ( !msg.channel.permissionsFor(msg.client.user).has('MANAGE_WEBHOOKS') ) {
@@ -50,12 +70,13 @@ 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 );
 
+			button.url = new URL(`/guild/${msg.guild.id}/rcscript/new`, process.env.dashboard).href;
 			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( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+				if ( !wikinew ) return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, 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', {
 				responseType: 'text'
@@ -79,25 +100,25 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					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 );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_private') + wikihelp, {components}, true );
 					}
 					msg.reactEmoji('nowiki', true);
-					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 				}
 				wikinew.updateWiki(body.query.general);
 				if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) < 30 ) {
 					if ( reaction ) reaction.removeEmoji();
-					return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {}, true );
+					return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {components}, true );
 				}
 				if ( body.query.allmessages[0]['*'] !== msg.guild.id ) {
 					if ( reaction ) reaction.removeEmoji();
-					return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit') + '>', {}, true );
+					return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit') + '>', {components}, true );
 				}
 				return db.query( 'SELECT reason FROM blocklist WHERE wiki = $1', [wikinew.href] ).then( ({rows:[block]}) => {
 					if ( block ) {
 						console.log( '- This wiki is blocked: ' + block.reason );
 						if ( reaction ) reaction.removeEmoji();
-						return msg.replyMsg( ( block.reason ? lang.get('rcscript.blocked_reason', block.reason) : lang.get('rcscript.blocked') ), {}, true );
+						return msg.replyMsg( ( block.reason ? lang.get('rcscript.blocked_reason', block.reason) : lang.get('rcscript.blocked') ), {components}, true );
 					}
 					if ( wikinew.isFandom(false) ) return got.get( wikinew + 'wikia.php?controller=DiscussionPost&method=getPosts&includeCounters=false&limit=1&format=json&cache=' + Date.now(), {
 						headers: {
@@ -136,16 +157,16 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 							db.query( 'INSERT INTO rcgcdw(guild, configid, webhook, wiki, lang, display, postid) VALUES($1, $2, $3, $4, $5, $6, $7)', [msg.guild.id, new_configid, webhook.id + '/' + webhook.token, wikinew.href, webhook_lang.lang, ( msg.showEmbed() ? 1 : 0 ), ( enableFeeds ? null : '-1' )] ).then( () => {
 								console.log( '- RcGcDw successfully added.' );
 								if ( reaction ) reaction.removeEmoji();
-								msg.replyMsg( lang.get('rcscript.added') + ' <' + wikinew + '>\n`' + prefix + 'rcscript' + ( rows.length ? ' ' + new_configid : '' ) + '`', {}, true );
+								msg.replyMsg( lang.get('rcscript.added') + ' <' + wikinew + '>\n`' + prefix + 'rcscript' + ( rows.length ? ' ' + new_configid : '' ) + '`', {components}, true );
 							}, dberror => {
 								console.log( '- Error while adding the RcGcDw: ' + dberror );
 								if ( reaction ) reaction.removeEmoji();
-								msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+								msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 							} );
 						}, error => {
 							console.log( '- Error while creating the webhook: ' + error );
 							if ( reaction ) reaction.removeEmoji();
-							msg.replyMsg( lang.get('rcscript.webhook_failed'), {}, true );
+							msg.replyMsg( lang.get('rcscript.webhook_failed'), {components}, true );
 						} );
 					}
 				}, dberror => {
@@ -157,14 +178,14 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				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' || error.message === 'self signed certificate' ) {
 					console.log( '- Error while testing the wiki: No HTTPS' );
-					return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {components}, 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 );
+					return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {components}, true );
 				}
 				msg.reactEmoji('nowiki', true);
-				return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+				return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 			} ) );
 		}
 
@@ -195,34 +216,38 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 						webhook.send( webhook_lang.get('deleted') ).catch(log_error).finally( () => {
 							webhook.delete(lang.get('rcscript.audit_reason_delete')).catch(log_error);
 						} );
-						msg.replyMsg( lang.get('rcscript.deleted'), {}, true );
+						msg.replyMsg( lang.get('rcscript.deleted'), {components}, true );
 					}, dberror => {
 						console.log( '- Error while removing the RcGcDw: ' + dberror );
-						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						button.url = new URL(`/guild/${msg.guild.id}/rcscript/${selected_row.configid}`, process.env.dashboard).href;
+						msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					} );
 				}, error => {
 					log_error(error);
 					if ( error.name === 'DiscordAPIError' && ['Unknown Webhook', 'Invalid Webhook Token'].includes( error.message ) ) {
-						return msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						button.url = new URL(`/guild/${msg.guild.id}/rcscript/${selected_row.configid}`, process.env.dashboard).href;
+						return msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					}
 					db.query( 'DELETE FROM rcgcdw WHERE webhook = $1', [selected_row.webhook] ).then( () => {
 						console.log( '- RcGcDw successfully removed.' );
-						msg.replyMsg( lang.get('rcscript.deleted'), {}, true );
+						msg.replyMsg( lang.get('rcscript.deleted'), {components}, true );
 					}, dberror => {
 						console.log( '- Error while removing the RcGcDw: ' + dberror );
-						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						button.url = new URL(`/guild/${msg.guild.id}/rcscript/${selected_row.configid}`, process.env.dashboard).href;
+						msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					} );
 				} );
 			}
+			button.url = new URL(`/guild/${msg.guild.id}/rcscript/${selected_row.configid}`, process.env.dashboard).href;
 			if ( args[0] === 'wiki' ) {
 				if ( !args[1] ) {
-					return msg.replyMsg( lang.get('rcscript.current_wiki') + ' <' + selected_row.wiki + '>\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki'), {}, true );
+					return msg.replyMsg( lang.get('rcscript.current_wiki') + ' <' + selected_row.wiki + '>\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki'), {components}, true );
 				}
 				if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 
 				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( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+				if ( !wikinew ) return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, 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 => {
@@ -245,26 +270,26 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 						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 );
+							return msg.replyMsg( lang.get('settings.wikiinvalid_private') + wikihelp, {components}, true );
 						}
 						msg.reactEmoji('nowiki', true);
-						return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 					}
 					wikinew.updateWiki(body.query.general);
 					if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) <= 30 ) {
 						console.log( '- This wiki is using ' + body.query.general.generator + '.' );
 						if ( reaction ) reaction.removeEmoji();
-						return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {}, true );
+						return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {components}, true );
 					}
 					if ( body.query.allmessages[0]['*'] !== msg.guild.id ) {
 						if ( reaction ) reaction.removeEmoji();
-						return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit') + '>', {}, true );
+						return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit') + '>', {components}, true );
 					}
 					return db.query( 'SELECT reason FROM blocklist WHERE wiki = $1', [wikinew.href] ).then( ({rows:[block]}) => {
 						if ( block ) {
 							console.log( '- This wiki is blocked: ' + block.reason );
 							if ( reaction ) reaction.removeEmoji();
-							return msg.replyMsg( ( block.reason ? lang.get('rcscript.blocked_reason', block.reason) : lang.get('rcscript.blocked') ), {}, true );
+							return msg.replyMsg( ( block.reason ? lang.get('rcscript.blocked_reason', block.reason) : lang.get('rcscript.blocked') ), {components}, true );
 						}
 						if ( wikinew.isFandom(false) ) return got.get( wikinew + 'wikia.php?controller=DiscussionPost&method=getPosts&includeCounters=false&limit=1&format=json&cache=' + Date.now(), {
 							headers: {
@@ -294,11 +319,11 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 							db.query( 'UPDATE rcgcdw SET wiki = $1, rcid = $2, postid = $3 WHERE webhook = $4', [wikinew.href, null, ( enableFeeds ? null : '-1' ), selected_row.webhook] ).then( () => {
 								console.log( '- RcGcDw successfully updated.' );
 								if ( reaction ) reaction.removeEmoji();
-								msg.replyMsg( lang.get('rcscript.updated_wiki') + ' <' + wikinew + '>\n`' + cmd + '`', {}, true );
+								msg.replyMsg( lang.get('rcscript.updated_wiki') + ' <' + wikinew + '>\n`' + cmd + '`', {components}, true );
 							}, dberror => {
 								console.log( '- Error while updating the RcGcDw: ' + dberror );
 								if ( reaction ) reaction.removeEmoji();
-								msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+								msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 							} );
 						}
 					}, dberror => {
@@ -310,23 +335,23 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					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' || error.message === 'self signed certificate' ) {
 						console.log( '- Error while testing the wiki: No HTTPS' );
-						return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {components}, 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 );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {components}, true );
 					}
 					msg.reactEmoji('nowiki', true);
-					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 				} ) );
 			}
 			if ( args[0] === 'lang' || args[0] === 'language' ) {
 				if ( !args[1] ) {
-					return msg.replyMsg( lang.get('rcscript.current_lang') + ' `' + allLangs.names[selected_row.lang] + '`\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {files:( msg.uploadFiles() ? [`./RcGcDb/locale/widgets/${selected_row.lang}.png`] : [] )}, true );
+					return msg.replyMsg( lang.get('rcscript.current_lang') + ' `' + allLangs.names[selected_row.lang] + '`\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {files:( msg.uploadFiles() ? [`./RcGcDb/locale/widgets/${selected_row.lang}.png`] : [] ),components}, true );
 				}
 				if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 				if ( !allLangs.map.hasOwnProperty(args[1]) ) {
-					return msg.replyMsg( lang.get('settings.langinvalid') + '\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {}, true );
+					return msg.replyMsg( lang.get('settings.langinvalid') + '\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {components}, true );
 				}
 
 				msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
@@ -334,15 +359,15 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				}, log_error );
 				return db.query( 'UPDATE rcgcdw SET lang = $1 WHERE webhook = $2', [allLangs.map[args[1]], selected_row.webhook] ).then( () => {
 					console.log( '- RcGcDw successfully updated.' );
-					msg.replyMsg( lang.get('rcscript.updated_lang') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n`' + cmd + '`', {files:( msg.uploadFiles() ? [`./RcGcDb/locale/widgets/${allLangs.map[args[1]]}.png`] : [] )}, true );
+					msg.replyMsg( lang.get('rcscript.updated_lang') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n`' + cmd + '`', {files:( msg.uploadFiles() ? [`./RcGcDb/locale/widgets/${allLangs.map[args[1]]}.png`] : [] ),components}, true );
 				}, dberror => {
 					console.log( '- Error while updating the RcGcDw: ' + dberror );
-					msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+					msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 				} );
 			}
 			if ( args[0] === 'display' ) {
 				if ( !args[1] || !display_types.includes( args[1] ) ) {
-					return msg.replyMsg( lang.get('rcscript.current_display') + ' `' + display_types[selected_row.display] + '`\n`' + cmd + ' display (' + display.join('|') + ')`\n' + display.map( display_type => '`' + display_type + '`: ' + lang.get('rcscript.help_display_' + display_type) ).join('\n'), {}, true );
+					return msg.replyMsg( lang.get('rcscript.current_display') + ' `' + display_types[selected_row.display] + '`\n`' + cmd + ' display (' + display.join('|') + ')`\n' + display.map( display_type => '`' + display_type + '`: ' + lang.get('rcscript.help_display_' + display_type) ).join('\n'), {components}, true );
 				}
 				if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 				if ( !display.includes( args[1] ) ) {
@@ -354,10 +379,10 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				}, log_error );
 				return db.query( 'UPDATE rcgcdw SET display = $1 WHERE webhook = $2', [display_types.indexOf(args[1]), selected_row.webhook] ).then( () => {
 					console.log( '- RcGcDw successfully updated.' );
-					msg.replyMsg( lang.get('rcscript.updated_display') + ' `' + args[1] + '`\n`' + cmd + '`', {}, true );
+					msg.replyMsg( lang.get('rcscript.updated_display') + ' `' + args[1] + '`\n`' + cmd + '`', {components}, true );
 				}, dberror => {
 					console.log( '- Error while updating the RcGcDw: ' + dberror );
-					msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+					msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 				} );
 			}
 			if ( new Wiki(selected_row.wiki).isFandom(false) && args[0] === 'feeds' ) {
@@ -369,41 +394,41 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 						}, log_error );
 						return db.query( 'UPDATE rcgcdw SET rcid = $1 WHERE webhook = $2', [null, selected_row.webhook] ).then( () => {
 							console.log( '- RcGcDw successfully updated.' );
-							msg.replyMsg( lang.get('rcscript.enabled_rc') + '\n`' + cmd + '`', {}, true );
+							msg.replyMsg( lang.get('rcscript.enabled_rc') + '\n`' + cmd + '`', {components}, true );
 						}, dberror => {
 							console.log( '- Error while updating the RcGcDw: ' + dberror );
-							msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+							msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 						} );
 					}
 
 					if ( selected_row.postid === '-1' ) {
-						return msg.replyMsg( lang.get('rcscript.all_inactive') + '\n\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`', {}, true );
+						return msg.replyMsg( lang.get('rcscript.all_inactive') + '\n\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`', {components}, true );
 					}
 					msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
 						webhook.send( webhook_lang.get('disabled_rc') ).catch(log_error);
 					}, log_error );
 					return db.query( 'UPDATE rcgcdw SET rcid = $1 WHERE webhook = $2', [-1, selected_row.webhook] ).then( () => {
 						console.log( '- RcGcDw successfully updated.' );
-						msg.replyMsg( lang.get('rcscript.disabled_rc') + '\n`' + cmd + '`', {}, true );
+						msg.replyMsg( lang.get('rcscript.disabled_rc') + '\n`' + cmd + '`', {components}, true );
 					}, dberror => {
 						console.log( '- Error while updating the RcGcDw: ' + dberror );
-						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					} );
 				}
 
 				if ( selected_row.postid !== '-1' ) {
 					if ( selected_row.rcid === -1 ) {
-						return msg.replyMsg( lang.get('rcscript.all_inactive') + '\n\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`', {}, true );
+						return msg.replyMsg( lang.get('rcscript.all_inactive') + '\n\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`', {components}, true );
 					}
 					msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
 						webhook.send( webhook_lang.get('disabled_feeds') ).catch(log_error);
 					}, log_error );
 					return db.query( 'UPDATE rcgcdw SET postid = $1 WHERE webhook = $2', ['-1', selected_row.webhook] ).then( () => {
 						console.log( '- RcGcDw successfully updated.' );
-						msg.replyMsg( lang.get('rcscript.disabled_feeds') + '\n`' + cmd + '`', {}, true );
+						msg.replyMsg( lang.get('rcscript.disabled_feeds') + '\n`' + cmd + '`', {components}, true );
 					}, dberror => {
 						console.log( '- Error while updating the RcGcDw: ' + dberror );
-						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					} );
 				}
 
@@ -416,7 +441,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					if ( dsresponse.statusCode !== 200 || !dsbody || dsbody.status === 404 ) {
 						if ( dsbody?.status !== 404 ) console.log( '- ' + dsresponse.statusCode + ': Error while checking for discussions: ' + dsbody?.title );
 						if ( reaction ) reaction.removeEmoji();
-						return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+						return msg.replyMsg( lang.get('rcscript.no_feeds'), {components}, true );
 					}
 					msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
 						webhook.send( webhook_lang.get('enabled_feeds') + '\n<' + selected_row.wiki + 'f>' ).catch(log_error);
@@ -424,16 +449,16 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					db.query( 'UPDATE rcgcdw SET postid = $1 WHERE webhook = $2', [null, selected_row.webhook] ).then( () => {
 						console.log( '- RcGcDw successfully updated.' );
 						if ( reaction ) reaction.removeEmoji();
-						msg.replyMsg( lang.get('rcscript.enabled_feeds') + '\n`' + cmd + '`', {}, true );
+						msg.replyMsg( lang.get('rcscript.enabled_feeds') + '\n`' + cmd + '`', {components}, true );
 					}, dberror => {
 						console.log( '- Error while updating the RcGcDw: ' + dberror );
 						if ( reaction ) reaction.removeEmoji();
-						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 					} );
 				}, error => {
 					console.log( '- Error while checking for discussions: ' + error );
 					if ( reaction ) reaction.removeEmoji();
-					return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+					return msg.replyMsg( lang.get('rcscript.no_feeds'), {components}, true );
 				} ) );
 			}
 
@@ -452,7 +477,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				return;
 			} ).then( channel => {
 				var text = lang.get('rcscript.current_selected', selected_row.configid);
-				text += `\n<${new URL(`/guild/${msg.guild.id}/rcscript/${selected_row.configid}`, process.env.dashboard).href}>\n`;
+				text += `\n<${button.url}>\n`;
 				text += '\n' + lang.get('rcscript.channel') + ' <#' + channel + '>\n';
 				text += '\n' + lang.get('rcscript.wiki') + ' <' + selected_row.wiki + '>';
 				text += '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n';
@@ -469,8 +494,8 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					text += '\n' + lang.get('rcscript.help_feeds') + '\n`' + cmd + ' feeds` ' + lang.get('rcscript.toggle') + '\n';
 				}
 				text += '\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`\n';
-				msg.replyMsg( text, {}, true );
-			}, () => msg.replyMsg( lang.get('rcscript.deleted'), {}, true ) );
+				msg.replyMsg( text, {components}, true );
+			}, () => msg.replyMsg( lang.get('rcscript.deleted'), {components}, true ) );
 		}
 
 		Promise.all(rows.map( row => msg.client.fetchWebhook(...row.webhook.split('/')).then( webhook => {
@@ -495,7 +520,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 			var text = '';
 			if ( rows.length ) {
 				text += lang.get('rcscript.current');
-				text += `\n<${new URL(`/guild/${msg.guild.id}/rcscript`, process.env.dashboard).href}>`;
+				text += `\n<${button.url}>`;
 				text += rows.map( row => {
 					var cmd = prefix + 'rcscript' + ( only ? '' : ' ' + row.configid );
 					var row_text = '\n';
@@ -522,10 +547,10 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 			}
 			else {
 				text += lang.get('rcscript.missing');
-				text += `\n<${new URL(`/guild/${msg.guild.id}/rcscript`, process.env.dashboard).href}>`;
+				text += `\n<${button.url}>`;
 			}
 			if ( rows.length < limit ) text += '\n\n' + lang.get('rcscript.add_more') + '\n`' + prefix + 'rcscript add ' + lang.get('rcscript.new_wiki') + '`';
-			msg.replyMsg( text, {split:true}, true );
+			msg.replyMsg( text, {split:true,components}, true );
 		} );
 	}, dberror => {
 		console.log( '- Error while getting the RcGcDw: ' + dberror );

+ 55 - 34
cmds/settings.js

@@ -25,10 +25,30 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}, defaultSettings);
 		var prefix = guild.prefix;
 		var inlinepage = ( lang.localNames.page || 'page' );
+		var button = {
+			type: 2,
+			style: 5,
+			label: lang.get('settings.button'),
+			emoji: {
+				id: '588723748757307403',
+				name: 'wikibot',
+				animated: false
+			},
+			url: new URL(`/guild/${msg.guild.id}/settings`, process.env.dashboard).href,
+			disabled: false
+		};
+		var components = [
+			{
+				type: 1,
+				components: [
+					button
+				]
+			}
+		];
 		var text = lang.get('settings.missing', '`' + prefix + 'settings lang`', '`' + prefix + 'settings wiki`');
 		if ( rows.length ) {
 			text = lang.get('settings.current');
-			text += `\n<${new URL(`/guild/${msg.guild.id}/settings`, process.env.dashboard).href}>`;
+			text += `\n<${button.url}>`;
 			text += '\n' + lang.get('settings.currentlang') + ' `' + allLangs.names[guild.lang] + '` - `' + prefix + 'settings lang`';
 			if ( patreons[msg.guild.id] ) text += '\n' + lang.get('settings.currentprefix') + ' `' + prefix + '` - `' + prefix + 'settings prefix`';
 			text += '\n' + lang.get('settings.currentrole') + ' ' + ( guild.role ? `<@&${guild.role}>` : '@everyone' ) + ' - `' + prefix + 'settings role`';
@@ -40,21 +60,22 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}
 		
 		if ( !args.length ) {
-			return msg.replyMsg( text, {split:true}, true );
+			return msg.replyMsg( text, {split:true,components}, true );
 		}
 		
 		var prelang = '';
 		args[0] = args[0].toLowerCase();
 		if ( args[0] === 'channel' ) {
 			prelang = 'channel ';
-			if ( !rows.length ) return msg.replyMsg( text, {split:true}, true );
+			if ( !rows.length ) return msg.replyMsg( text, {split:true,components}, true );
 			
 			var channel = rows.find( row => row.channel === msg.channel.id );
 			if ( !channel ) channel = Object.assign({}, rows.find( row => {
 				return ( row.channel === '#' + msg.channel.parentID );
 			} ) || guild, {channel: msg.channel.id});
 			text = lang.get('settings.' + prelang + 'current');
-			text += `\n<${new URL(`/guild/${msg.guild.id}/settings/${msg.channel.id}`, process.env.dashboard).href}>`;
+			button.url = new URL(`/guild/${msg.guild.id}/settings/${msg.channel.id}`, process.env.dashboard).href;
+			text += `\n<${button.url}>`;
 			if ( patreons[msg.guild.id] ) {
 				text += '\n' + lang.get('settings.currentlang') + ' `' + allLangs.names[channel.lang] + '` - `' + prefix + 'settings channel lang`';
 				text += '\n' + lang.get('settings.currentrole') + ' ' + ( channel.role ? `<@&${channel.role}>` : '@everyone' ) + ' - `' + prefix + 'settings channel role`';
@@ -62,7 +83,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			}
 			text += '\n' + lang.get('settings.currentwiki') + ' ' + channel.wiki + ' - `' + prefix + 'settings channel wiki`';
 			
-			if ( !args[1] ) return msg.replyMsg( text, {}, true );
+			if ( !args[1] ) return msg.replyMsg( text, {components}, true );
 			
 			args[0] = args[1].toLowerCase();
 			args[1] = args.slice(2).join(' ').toLowerCase().trim().replace( /^<\s*(.*)\s*>$/, '$1' );
@@ -73,15 +94,15 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			prelang += 'wiki';
 			var wikihelp = '\n' + lang.get('settings.wikihelp', prefix + 'settings ' + prelang);
 			if ( !args[1] ) {
-				if ( !rows.length ) return msg.replyMsg( lang.get('settings.wikimissing') + wikihelp, {}, true );
-				else return msg.replyMsg( lang.get('settings.' + prelang) + ' ' + ( channel || guild ).wiki + wikihelp, {}, true );
+				if ( !rows.length ) return msg.replyMsg( lang.get('settings.wikimissing') + wikihelp, {components}, true );
+				else return msg.replyMsg( lang.get('settings.' + prelang) + ' ' + ( channel || guild ).wiki + wikihelp, {components}, true );
 			}
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 			var wikinew = Wiki.fromInput(args[1]);
 			if ( !wikinew ) {
 				var text = lang.get('settings.wikiinvalid') + wikihelp;
 				//text += '\n\n' + lang.get('settings.foundwikis') + '\n' + sites.map( site => site.wiki_display_name + ': `' + site.wiki_domain + '`' ).join('\n');
-				return msg.replyMsg( text, {split:true}, true );
+				return msg.replyMsg( text, {split:true,components}, true );
 			}
 			return msg.reactEmoji('⏳', true).then( reaction => {
 				got.get( wikinew + 'api.php?&action=query&meta=siteinfo&siprop=general&format=json', {
@@ -106,10 +127,10 @@ function cmd_settings(lang, msg, args, line, wiki) {
 						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 );
+							return msg.replyMsg( lang.get('settings.wikiinvalid_private') + wikihelp, {components}, true );
 						}
 						msg.reactEmoji('nowiki', true);
-						return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 					}
 					wikinew.updateWiki(body.query.general);
 					var embed;
@@ -138,7 +159,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 						if ( !rows.includes( channel ) ) {
 							if ( channel.wiki === wikinew.href ) {
 								if ( reaction ) reaction.removeEmoji();
-								return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + channel.wiki + wikihelp, {embed}, true );
+								return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + channel.wiki + wikihelp, {embed,components}, true );
 							}
 							sql = 'INSERT INTO discord(wiki, guild, channel, lang, role, inline, prefix) VALUES($1, $2, $3, $4, $5, $6, $7)';
 							sqlargs.push(guild.lang, guild.role, guild.inline, guild.prefix);
@@ -155,7 +176,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 						}
 						if ( channel || !rows.some( row => row.channel === msg.channel.id ) ) wiki = new Wiki(wikinew);
 						if ( reaction ) reaction.removeEmoji();
-						msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + wikinew + wikihelp, {embed}, true );
+						msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + wikinew + wikihelp, {embed,components}, true );
 						var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
 						if ( channels.length ) db.query( 'DELETE FROM discord WHERE channel IN (' + channels.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', channels ).then( () => {
 							console.log( '- Settings successfully removed.' );
@@ -164,21 +185,21 @@ function cmd_settings(lang, msg, args, line, wiki) {
 						} );
 					}, dberror => {
 						console.log( '- Error while editing the settings: ' + dberror );
-						msg.replyMsg( lang.get('settings.save_failed'), {embed}, true );
+						msg.replyMsg( lang.get('settings.save_failed'), {embed,components}, true );
 						if ( reaction ) reaction.removeEmoji();
 					} );
 				}, ferror => {
 					if ( reaction ) reaction.removeEmoji();
 					if ( ferror.message?.startsWith( 'connect ECONNREFUSED ' ) || ferror.message?.startsWith( 'Hostname/IP does not match certificate\'s altnames: ' ) || ferror.message === 'certificate has expired' || ferror.message === 'self signed certificate' ) {
 						console.log( '- Error while testing the wiki: No HTTPS' );
-						return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_http') + wikihelp, {components}, true );
 					}
 					console.log( '- Error while testing the wiki: ' + ferror );
 					if ( ferror.message === `Timeout awaiting 'request' for ${got.defaults.options.timeout.request}ms` ) {
-						return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {}, true );
+						return msg.replyMsg( lang.get('settings.wikiinvalid_timeout') + wikihelp, {components}, true );
 					}
 					msg.reactEmoji('nowiki', true);
-					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
+					return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {components}, true );
 				} );
 			} );
 		}
@@ -188,11 +209,11 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			prelang += 'lang';
 			var langhelp = '\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`';
 			if ( !args[1] ) {
-				return msg.replyMsg( lang.get('settings.' + prelang) + ' `' + allLangs.names[( channel || guild ).lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${( channel || guild ).lang}.png`] : [] )}, true );
+				return msg.replyMsg( lang.get('settings.' + prelang) + ' `' + allLangs.names[( channel || guild ).lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${( channel || guild ).lang}.png`] : [] ),components}, true );
 			}
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 			if ( !allLangs.map.hasOwnProperty(args[1]) ) {
-				return msg.replyMsg( lang.get('settings.langinvalid') + langhelp, {}, true );
+				return msg.replyMsg( lang.get('settings.langinvalid') + langhelp, {components}, true );
 			}
 			var sql = 'UPDATE discord SET lang = $1 WHERE guild = $2 AND lang = $3';
 			var sqlargs = [allLangs.map[args[1]], msg.guild.id, guild.lang];
@@ -205,7 +226,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				sqlargs[2] = msg.channel.id;
 				if ( !rows.includes( channel ) ) {
 					if ( channel.lang === allLangs.map[args[1]] ) {
-						return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[channel.lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${channel.lang}.png`] : [] )}, true );
+						return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[channel.lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${channel.lang}.png`] : [] ),components}, true );
 					}
 					sql = 'INSERT INTO discord(lang, guild, channel, wiki, role, inline, prefix) VALUES($1, $2, $3, $4, $5, $6, $7)';
 					sqlargs.push(guild.wiki, guild.role, guild.inline, guild.prefix);
@@ -222,7 +243,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 					if ( voice[msg.guild.id] ) voice[msg.guild.id] = guild.lang;
 				}
 				if ( channel || !patreons[msg.guild.id] || !rows.some( row => row.channel === msg.channel.id ) ) lang = new Lang(allLangs.map[args[1]]);
-				msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`', {files:( msg.uploadFiles() ? [`./i18n/widgets/${allLangs.map[args[1]]}.png`] : [] )}, true );
+				msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`', {files:( msg.uploadFiles() ? [`./i18n/widgets/${allLangs.map[args[1]]}.png`] : [] ),components}, true );
 				var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
 				if ( channels.length ) db.query( 'DELETE FROM discord WHERE channel IN (' + channels.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', channels ).then( () => {
 					console.log( '- Settings successfully removed.' );
@@ -231,7 +252,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				} );
 			}, dberror => {
 				console.log( '- Error while editing the settings: ' + dberror );
-				msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+				msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 			} );
 		}
 		
@@ -240,7 +261,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			prelang += 'role';
 			var rolehelp = '\n' + lang.get('settings.rolehelp', prefix + 'settings ' + prelang);
 			if ( !args[1] ) {
-				return msg.replyMsg( lang.get('settings.' + prelang) + ' ' + ( ( channel || guild ).role ? `<@&${( channel || guild ).role}>` : '@everyone' ) + rolehelp, {}, true );
+				return msg.replyMsg( lang.get('settings.' + prelang) + ' ' + ( ( channel || guild ).role ? `<@&${( channel || guild ).role}>` : '@everyone' ) + rolehelp, {components}, true );
 			}
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 			var role = null;
@@ -250,7 +271,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				role = msg.guild.roles.cache.get(msg.guild.id);
 			}
 			if ( !role ) {
-				return msg.replyMsg( lang.get('settings.roleinvalid') + rolehelp, {}, true );
+				return msg.replyMsg( lang.get('settings.roleinvalid') + rolehelp, {components}, true );
 			}
 			role = ( role.id === msg.guild.id ? null : role.id );
 			var sql = 'UPDATE discord SET role = $1 WHERE guild = $2';
@@ -264,7 +285,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				sqlargs.push(msg.channel.id);
 				if ( !rows.includes( channel ) ) {
 					if ( channel.role === role ) {
-						return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + ( channel.role ? `<@&${channel.role}>` : '@everyone' ) + rolehelp, {}, true );
+						return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + ( channel.role ? `<@&${channel.role}>` : '@everyone' ) + rolehelp, {components}, true );
 					}
 					sql = 'INSERT INTO discord(role, guild, channel, wiki, lang, inline, prefix) VALUES($1, $2, $3, $4, $5, $6, $7)';
 					sqlargs.push(guild.wiki, guild.lang, guild.inline, guild.prefix);
@@ -284,7 +305,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 					} );
 					guild.role = role;
 				}
-				msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + ( role ? `<@&${role}>` : '@everyone' ) + rolehelp, {}, true );
+				msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + ( role ? `<@&${role}>` : '@everyone' ) + rolehelp, {components}, true );
 				var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
 				if ( channels.length ) db.query( 'DELETE FROM discord WHERE channel IN (' + channels.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', channels ).then( () => {
 					console.log( '- Settings successfully removed.' );
@@ -293,7 +314,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				} );
 			}, dberror => {
 				console.log( '- Error while editing the settings: ' + dberror );
-				msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+				msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 			} );
 		}
 		
@@ -304,11 +325,11 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			var prefixhelp = '\n' + lang.get('settings.prefixhelp', prefix + 'settings prefix');
 			args[1] = args[1].replace( /(?<!\\)_$/, ' ' ).replace( /\\([_\W])/g, '$1' );
 			if ( !args[1].trim() ) {
-				return msg.replyMsg( lang.get('settings.prefix') + ' `' + prefix + '`' + prefixhelp, {}, true );
+				return msg.replyMsg( lang.get('settings.prefix') + ' `' + prefix + '`' + prefixhelp, {components}, true );
 			}
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 			if ( args[1].includes( '`' ) || args[1].includes( '\\' ) || args[1].length > 100 ) {
-				return msg.replyMsg( lang.get('settings.prefixinvalid') + prefixhelp, {}, true );
+				return msg.replyMsg( lang.get('settings.prefixinvalid') + prefixhelp, {components}, true );
 			}
 			if ( args[1] === 'reset' || args[1] === 'default' ) args[1] = process.env.prefix;
 			var sql = 'UPDATE discord SET prefix = $1 WHERE guild = $2';
@@ -321,10 +342,10 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				console.log( '- Settings successfully updated.' );
 				guild.prefix = args[1];
 				msg.client.shard.broadcastEval( `global.patreons['${msg.guild.id}'] = '${args[1]}'` );
-				msg.replyMsg( lang.get('settings.prefixchanged') + ' `' + args[1] + '`\n' + lang.get('settings.prefixhelp', args[1] + 'settings prefix'), {}, true );
+				msg.replyMsg( lang.get('settings.prefixchanged') + ' `' + args[1] + '`\n' + lang.get('settings.prefixhelp', args[1] + 'settings prefix'), {components}, true );
 			}, dberror => {
 				console.log( '- Error while editing the settings: ' + dberror );
-				msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+				msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 			} );
 		}
 		
@@ -334,7 +355,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			var toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
 			var inlinehelp = '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage);
 			if ( args[1] !== 'toggle' ) {
-				return msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang) + inlinehelp, {}, true );
+				return msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang) + inlinehelp, {components}, true );
 			}
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
 			var value = ( ( channel || guild ).inline ? null : 1 );
@@ -362,7 +383,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 					guild.inline = value;
 				}
 				toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
-				msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang + 'changed') + '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage), {}, true );
+				msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang + 'changed') + '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage), {components}, true );
 				var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
 				if ( channels.length ) db.query( 'DELETE FROM discord WHERE channel IN (' + channels.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', channels ).then( () => {
 					console.log( '- Settings successfully removed.' );
@@ -371,11 +392,11 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				} );
 			}, dberror => {
 				console.log( '- Error while editing the settings: ' + dberror );
-				msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+				msg.replyMsg( lang.get('settings.save_failed'), {components}, true );
 			} );
 		}
 		
-		return msg.replyMsg( text, {split:true}, true );
+		return msg.replyMsg( text, {split:true,components}, true );
 	}, dberror => {
 		console.log( '- Error while getting the settings: ' + dberror );
 		msg.reactEmoji('error', true);

+ 53 - 30
cmds/verification.js

@@ -25,13 +25,34 @@ function cmd_verification(lang, msg, args, line, wiki) {
 	
 	db.query( 'SELECT configid, channel, role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 ORDER BY configid ASC', [msg.guild.id] ).then( ({rows}) => {
 		var prefix = ( patreons[msg.guild.id] || process.env.prefix );
+		var button = {
+			type: 2,
+			style: 5,
+			label: lang.get('settings.button'),
+			emoji: {
+				id: '588723748757307403',
+				name: 'wikibot',
+				animated: false
+			},
+			url: new URL(`/guild/${msg.guild.id}/verification`, process.env.dashboard).href,
+			disabled: false
+		};
+		var components = [
+			{
+				type: 1,
+				components: [
+					button
+				]
+			}
+		];
 		if ( args[0] && args[0].toLowerCase() === 'add' ) {
 			var limit = verificationLimit[( patreons[msg.guild.id] ? 'patreon' : 'default' )];
 			if ( rows.length >= limit ) return msg.replyMsg( lang.get('verification.max_entries'), {}, true );
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
+			button.url = new URL(`/guild/${msg.guild.id}/verification/new`, process.env.dashboard).href;
 			var roles = args.slice(1).join(' ').split('|').map( role => role.replace( /^\s*<?\s*(.*?)\s*>?\s*$/, '$1' ) ).filter( role => role.length );
-			if ( !roles.length ) return msg.replyMsg( lang.get('verification.no_role') + '\n`' + prefix + 'verification add ' + lang.get('verification.new_role') + '`', {}, true );
-			if ( roles.length > 10 ) return msg.replyMsg( lang.get('verification.role_max'), {}, true );
+			if ( !roles.length ) return msg.replyMsg( lang.get('verification.no_role') + '\n`' + prefix + 'verification add ' + lang.get('verification.new_role') + '`', {components}, true );
+			if ( roles.length > 10 ) return msg.replyMsg( lang.get('verification.role_max'), {components}, true );
 			roles = roles.map( role => {
 				var new_role = '';
 				if ( /^\d+$/.test(role) ) new_role = msg.guild.roles.cache.get(role);
@@ -39,8 +60,8 @@ function cmd_verification(lang, msg, args, line, wiki) {
 				if ( !new_role ) new_role = msg.guild.roles.cache.find( gc => gc.name.toLowerCase() === role.toLowerCase().replace( /^@/, '' ) );
 				return new_role;
 			} );
-			if ( roles.some( role => !role ) ) return msg.replyMsg( lang.get('verification.role_missing'), {}, true );
-			if ( roles.some( role => role.managed ) ) return msg.replyMsg( lang.get('verification.role_managed'), {}, true );
+			if ( roles.some( role => !role ) ) return msg.replyMsg( lang.get('verification.role_missing'), {components}, true );
+			if ( roles.some( role => role.managed ) ) return msg.replyMsg( lang.get('verification.role_managed'), {components}, true );
 			roles = roles.map( role => role.id ).join('|');
 			var new_configid = 1;
 			for ( let i of rows.map( row => row.configid ) ) {
@@ -64,10 +85,10 @@ function cmd_verification(lang, msg, args, line, wiki) {
 				}, error => {
 					console.log( '- Error while enabling the slash command: ' + error );
 				} );
-				msg.replyMsg( lang.get('verification.added') + formatVerification(false, false, {configid: new_configid, role: roles}), {}, true );
+				msg.replyMsg( lang.get('verification.added') + formatVerification(false, false, {configid: new_configid, role: roles}), {components}, true );
 			}, dberror => {
 				console.log( '- Error while adding the verification: ' + dberror );
-				msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+				msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 			} );
 		}
 		if ( !rows.some( row => row.configid.toString() === args[0] ) ) {
@@ -78,15 +99,15 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			var text = '';
 			if ( rows.length ) {
 				text += lang.get('verification.current');
-				text += `\n<${new URL(`/guild/${msg.guild.id}/verification`, process.env.dashboard).href}>`;
+				text += `\n<${button.url}>`;
 				text += rows.map( row => formatVerification(false, true, row) ).join('');
 			}
 			else {
 				text += lang.get('verification.missing');
-				text += `\n<${new URL(`/guild/${msg.guild.id}/verification`, process.env.dashboard).href}>`;
+				text += `\n<${button.url}>`;
 			}
 			text += '\n\n' + lang.get('verification.add_more') + '\n`' + prefix + 'verification add ' + lang.get('verification.new_role') + '`';
-			return msg.sendChannel( '<@' + msg.author.id + '>, ' + text, {split:true}, true );
+			return msg.sendChannel( '<@' + msg.author.id + '>, ' + text, {split:true,components}, true );
 		}
 		var row = rows.find( row => row.configid.toString() === args[0] );
 		if ( args[1] ) args[1] = args[1].toLowerCase();
@@ -103,12 +124,14 @@ function cmd_verification(lang, msg, args, line, wiki) {
 				}, error => {
 					console.log( '- Error while disabling the slash command: ' + error );
 				} );
-				msg.replyMsg( lang.get('verification.deleted'), {}, true );
+				msg.replyMsg( lang.get('verification.deleted'), {components}, true );
 			}, dberror => {
 				console.log( '- Error while removing the verification: ' + dberror );
-				msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+				button.url = new URL(`/guild/${msg.guild.id}/verification/${row.configid}`, process.env.dashboard).href;
+				msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 			} );
 		}
+		button.url = new URL(`/guild/${msg.guild.id}/verification/${row.configid}`, process.env.dashboard).href;
 		if ( args[1] === 'rename' && !args.slice(2).join('') ) {
 			if ( !row.rename && !msg.guild.me.permissions.has('MANAGE_NICKNAMES') ) {
 				console.log( msg.guild.id + ': Missing permissions - MANAGE_NICKNAMES' );
@@ -118,10 +141,10 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			return db.query( 'UPDATE verification SET rename = $1 WHERE guild = $2 AND configid = $3', [( row.rename ? 0 : 1 ), msg.guild.id, row.configid] ).then( () => {
 				console.log( '- Verification successfully updated.' );
 				row.rename = ( row.rename ? 0 : 1 );
-				msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true}, true );
+				msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true,components}, true );
 			}, dberror => {
 				console.log( '- Error while updating the verification: ' + dberror );
-				msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+				msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 			} );
 		}
 		if ( args[2] ) {
@@ -129,7 +152,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			args[2] = args.slice(2).join(' ').replace( /^\s*<?\s*(.*?)\s*>?\s*$/, '$1' );
 			if ( args[1] === 'channel' ) {
 				var channels = args[2].replace( /\s*>?\s*[,|]\s*<?\s*/g, '|' ).split('|').filter( channel => channel.length );
-				if ( channels.length > 10 ) return msg.replyMsg( lang.get('verification.channel_max'), {}, true );
+				if ( channels.length > 10 ) return msg.replyMsg( lang.get('verification.channel_max'), {components}, true );
 				channels = channels.map( channel => {
 					var new_channel = '';
 					if ( /^\d+$/.test(channel) ) new_channel = msg.guild.channels.cache.filter( tc => tc.isGuild() ).get(channel);
@@ -137,20 +160,20 @@ function cmd_verification(lang, msg, args, line, wiki) {
 					if ( !new_channel ) new_channel = msg.guild.channels.cache.filter( gc => gc.isGuild() ).find( gc => gc.name.toLowerCase() === channel.toLowerCase().replace( /^#/, '' ) );
 					return new_channel;
 				} );
-				if ( channels.some( channel => !channel ) ) return msg.replyMsg( lang.get('verification.channel_missing'), {}, true );
+				if ( channels.some( channel => !channel ) ) return msg.replyMsg( lang.get('verification.channel_missing'), {components}, true );
 				channels = channels.map( channel => channel.id ).join('|');
 				if ( channels.length ) return db.query( 'UPDATE verification SET channel = $1 WHERE guild = $2 AND configid = $3', ['|' + channels + '|', msg.guild.id, row.configid] ).then( () => {
 					console.log( '- Verification successfully updated.' );
 					row.channel = '|' + channels + '|';
-					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true}, true );
+					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true,components}, true );
 				}, dberror => {
 					console.log( '- Error while updating the verification: ' + dberror );
-					msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+					msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 				} );
 			}
 			if ( args[1] === 'role' ) {
 				var roles = args[2].replace( /\s*>?\s*[,|]\s*<?\s*/g, '|' ).split('|').filter( role => role.length );
-				if ( roles.length > 10 ) return msg.replyMsg( lang.get('verification.role_max'), {}, true );
+				if ( roles.length > 10 ) return msg.replyMsg( lang.get('verification.role_max'), {components}, true );
 				roles = roles.map( role => {
 					var new_role = null;
 					if ( /^\d+$/.test(role) ) new_role = msg.guild.roles.cache.get(role);
@@ -158,31 +181,31 @@ function cmd_verification(lang, msg, args, line, wiki) {
 					if ( !new_role ) new_role = msg.guild.roles.cache.find( gc => gc.name.toLowerCase() === role.toLowerCase().replace( /^@/, '' ) );
 					return new_role;
 				} );
-				if ( roles.some( role => !role ) ) return msg.replyMsg( lang.get('verification.role_missing'), {}, true );
-				if ( roles.some( role => role.managed || role.id === msg.guild.id ) ) return msg.replyMsg( lang.get('verification.role_managed'), {}, true );
+				if ( roles.some( role => !role ) ) return msg.replyMsg( lang.get('verification.role_missing'), {components}, true );
+				if ( roles.some( role => role.managed || role.id === msg.guild.id ) ) return msg.replyMsg( lang.get('verification.role_managed'), {components}, true );
 				roles = roles.map( role => role.id ).join('|');
 				if ( roles.length ) return db.query( 'UPDATE verification SET role = $1 WHERE guild = $2 AND configid = $3', [roles, msg.guild.id, row.configid] ).then( () => {
 					console.log( '- Verification successfully updated.' );
 					row.role = roles;
-					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true}, true );
+					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true,components}, true );
 				}, dberror => {
 					console.log( '- Error while updating the verification: ' + dberror );
-					msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+					msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 				} );
 			}
 			if ( ( ( args[1] === 'editcount' || args[1] === 'accountage' ) && /^\d+$/.test(args[2]) ) || ( args[1] === 'postcount' && /^(?:-?\d+|null)$/.test(args[2]) ) ) {
 				args[2] = parseInt(args[2], 10);
 				if ( isNaN(args[2]) ) args[2] = null;
 				if ( args[2] > 1000000 || args[2] < -1000000 ) {
-					return msg.replyMsg( lang.get('verification.value_too_high'), {}, true );
+					return msg.replyMsg( lang.get('verification.value_too_high'), {components}, true );
 				}
 				return db.query( 'UPDATE verification SET ' + args[1] + ' = $1 WHERE guild = $2 AND configid = $3', [args[2], msg.guild.id, row.configid] ).then( () => {
 					console.log( '- Verification successfully updated.' );
 					row[args[1]] = args[2];
-					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true}, true );
+					msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true,components}, true );
 				}, dberror => {
 					console.log( '- Error while updating the verification: ' + dberror );
-					msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+					msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 				} );
 			}
 			if ( args[1] === 'usergroup' ) {
@@ -192,8 +215,8 @@ function cmd_verification(lang, msg, args, line, wiki) {
 					usergroups = usergroups.slice(1);
 					and_or = 'AND|';
 				}
-				if ( usergroups.length > 10 ) return msg.replyMsg( lang.get('verification.usergroup_max'), {}, true );
-				if ( usergroups.some( usergroup => usergroup.length > 100 ) ) return msg.replyMsg( lang.get('verification.usergroup_too_long'), {}, true );
+				if ( usergroups.length > 10 ) return msg.replyMsg( lang.get('verification.usergroup_max'), {components}, true );
+				if ( usergroups.some( usergroup => usergroup.length > 100 ) ) return msg.replyMsg( lang.get('verification.usergroup_too_long'), {components}, true );
 				if ( usergroups.length ) return msg.reactEmoji('⏳').then( reaction => got.get( wiki + 'api.php?action=query&meta=allmessages&amprefix=group-&amincludelocal=true&amenableparser=true&format=json' ).then( response => {
 					var body = response.body;
 					if ( body && body.warnings ) log_warn(body.warnings);
@@ -229,19 +252,19 @@ function cmd_verification(lang, msg, args, line, wiki) {
 					db.query( 'UPDATE verification SET usergroup = $1 WHERE guild = $2 AND configid = $3', [and_or + usergroups, msg.guild.id, row.configid] ).then( () => {
 						console.log( '- Verification successfully updated.' );
 						row.usergroup = and_or + usergroups;
-						msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true}, true );
+						msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.updated') + formatVerification(), {split:true,components}, true );
 						
 						if ( reaction ) reaction.removeEmoji();
 					}, dberror => {
 						console.log( '- Error while updating the verification: ' + dberror );
-						msg.replyMsg( lang.get('verification.save_failed'), {}, true );
+						msg.replyMsg( lang.get('verification.save_failed'), {components}, true );
 						
 						if ( reaction ) reaction.removeEmoji();
 					} );
 				} ) );
 			}
 		}
-		return msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.current_selected', row.configid) + `\n<${new URL(`/guild/${msg.guild.id}/verification/${row.configid}`, process.env.dashboard).href}>` + formatVerification(true) +'\n\n' + lang.get('verification.delete_current') + '\n`' + prefix + 'verification ' + row.configid + ' delete`', {split:true}, true );
+		return msg.sendChannel( '<@' + msg.author.id + '>, ' + lang.get('verification.current_selected', row.configid) + `\n<${button.url}>` + formatVerification(true) +'\n\n' + lang.get('verification.delete_current') + '\n`' + prefix + 'verification ' + row.configid + ' delete`', {split:true,components}, true );
 		
 		function formatVerification(showCommands, hideNotice, {
 			configid,

+ 2 - 2
dashboard/i18n/en.json

@@ -31,7 +31,7 @@
     },
     "indexjs": {
         "invalid": {
-            "note_http": "The provided website doesn't use HTTPS!",
+            "note_http": "The provided website doesn't have a valid TLS/SSL certificate! For security reasons only wikis using HTTPS are supported.",
             "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!",
@@ -120,7 +120,7 @@
             "title": "Settings saved!"
         },
         "savefail": {
-            "note_http": "The provided website doesn't use HTTPS!",
+            "note_http": "The provided website doesn't have a valid TLS/SSL certificate! For security reasons only wikis using HTTPS are supported.",
             "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.",

+ 26 - 10
dashboard/src/index.js

@@ -24,6 +24,7 @@ function lang(message = '') {
 var baseSelect = document.getElementsByTagName('select');
 for ( var b = 0; b < baseSelect.length; b++ ) {
 	if ( baseSelect[b].id === 'wb-settings-lang' ) {
+		/** @type {HTMLImageElement} */
 		const langWidget = document.getElementById('wb-settings-lang-widget');
 		if ( langWidget ) {
 			var widgetPath = 'widgets';
@@ -44,9 +45,11 @@ for ( var b = 0; b < baseSelect.length; b++ ) {
 	}
 }
 
+/** @type {HTMLCollectionOf<HTMLButtonElement>} */
 var addmore = document.getElementsByClassName('addmore');
 for ( var j = 0; j < addmore.length; j++ ) {
 	addmore[j].onclick = function() {
+		/** @type {HTMLSelectElement} */
 		var clone = this.previousElementSibling.cloneNode(true);
 		clone.classList.add('wb-settings-additional-select');
 		clone.removeAttribute('id');
@@ -62,11 +65,11 @@ for ( var j = 0; j < addmore.length; j++ ) {
 	};
 }
 
-/**
- * @this HTMLSelectElement
- */
+/** @this HTMLSelectElement */
 function toggleOption() {
+	/** @type {HTMLOptionElement[]} */
 	var options = [];
+	/** @type {HTMLOptionElement[]} */
 	var selected = [];
 	var allSelect = this.parentNode.querySelectorAll('select');
 	allSelect.forEach( function(select) {
@@ -81,20 +84,21 @@ function toggleOption() {
 		button.hidden = true;
 	}
 	else button.hidden = false;
-	selected = selected.filter( function(option) {
+	var selectedValues = selected.filter( function(option) {
 		if ( option && option.value ) return true;
 		else return false;
 	} ).map( function(option) {
 		return option.value;
 	} );
 	options.forEach( function(option) {
-		if ( selected.includes( option.value ) && !option.selected ) {
+		if ( selectedValues.includes( option.value ) && !option.selected ) {
 			option.disabled = true;
 		}
 		else if ( option.disabled ) option.disabled = false;
 	} );
 }
 
+/** @type {HTMLInputElement} */
 const wiki = document.getElementById('wb-settings-wiki');
 if ( wiki ) {
 	wiki.addEventListener( 'input', function() {
@@ -107,7 +111,9 @@ if ( wiki ) {
 		}
 		else this.setCustomValidity('');
 	} );
+	/** @type {HTMLButtonElement} */
 	const wikicheck = document.getElementById('wb-settings-wiki-check');
+	/** @type {HTMLDivElement} */
 	const wikichecknotice = document.getElementById('wb-settings-wiki-check-notice');
 	if ( wikicheck && wikichecknotice ) {
 		wikicheck.onclick = function() {
@@ -186,7 +192,7 @@ if ( wiki ) {
 						wikichecknotice.append(noticeTitle, noticeText, noticeLink, ...noticeExtraParts);
 						return;
 					}
-					if ( response.RcGcDw !== document.location.pathname.split('/')[2] ) {
+					if ( response.RcGcDw !== document.location.pathname.split('/')[2] && ( document.location.pathname.split('/')[4] === 'new' || wiki.value !== wiki.defaultValue ) ) {
 						wikichecknotice.classList.add('notice-info');
 						var noticeTitle = document.createElement('b');
 						noticeTitle.textContent = lang('sysmessage.title');
@@ -248,10 +254,14 @@ if ( wiki ) {
 			} );
 		};
 	}
+	/** @type {HTMLInputElement} */
 	const feeds = document.getElementById('wb-settings-feeds');
 	if ( feeds ) {
+		/** @type {HTMLDivElement} */
 		const hidefeeds = document.getElementById('wb-settings-feeds-hide');
+		/** @type {HTMLInputElement} */
 		const feedsonly = document.getElementById('wb-settings-feeds-only');
+		/** @type {HTMLDivElement} */
 		const hidefeedsonly = document.getElementById('wb-settings-feeds-only-hide');
 		feeds.addEventListener( 'change', function() {
 			if ( this.checked ) {
@@ -278,9 +288,12 @@ if ( wiki ) {
 	}
 }
 
+/** @type {HTMLInputElement} */
 const usergroup = document.getElementById('wb-settings-usergroup');
 if ( usergroup ) {
+	/** @type {HTMLDivElement} */
 	const multigroup = document.getElementById('wb-settings-usergroup-multiple');
+	/** @type {HTMLDataListElement} */
 	const usergrouplist = document.getElementById('wb-settings-usergroup-list');
 	usergroup.addEventListener( 'input', function() {
 		if ( /\s*[,|]\s*$/.test(usergroup.value) ) {
@@ -299,6 +312,7 @@ if ( usergroup ) {
 			usergroup.style.minWidth = newWidth + 'px';
 		}
 		if ( usergroup.value.includes( ',' ) || usergroup.value.includes( '|' ) ) {
+			multigroup.style.display = '';
 			multigroup.style.visibility = '';
 			multigroup.disabled = false;
 		}
@@ -309,8 +323,10 @@ if ( usergroup ) {
 	} );
 }
 
+/** @type {NodeListOf<HTMLInputElement>} */
 const postcount = document.querySelectorAll('.wb-settings-postcount input');
 if ( postcount.length ) {
+	/** @type {HTMLDivElement} */
 	const postcountinput = document.getElementById('wb-settings-postcount-input');
 	postcount.forEach( function(radio) {
 		radio.addEventListener( 'change', function() {
@@ -324,6 +340,7 @@ if ( postcount.length ) {
 	} );
 }
 
+/** @type {HTMLInputElement} */
 const prefix = document.getElementById('wb-settings-prefix');
 if ( prefix ) prefix.addEventListener( 'input', function() {
 	if ( prefix.validity.patternMismatch ) {
@@ -343,10 +360,11 @@ if ( prefix ) prefix.addEventListener( 'input', function() {
 
 /** @type {HTMLSelectElement} */
 const addRole = document.getElementById('wb-settings-addrole');
+/** @type {HTMLButtonElement} */
 const addRoleButton = document.getElementById('wb-settings-addrole-add');
 if ( addRole && addRoleButton ) addRoleButton.onclick = function() {
 	if ( addRole.value ) {
-		var selectedRole = addRole.children.item(addRole.selectedIndex);
+		var selectedRole = addRole.selectedOptions.item(0);
 		var newPermission = document.createElement('div');
 		var selectedRoleInfo = selectedRole.textContent.split(' – ');
 		var newPermissionSpan = document.createElement('span');
@@ -445,9 +463,7 @@ if ( textAreas.length ) {
 		}
 	}
 
-	/**
-	 * @this HTMLTextAreaElement
-	 */
+	/** @this HTMLTextAreaElement */
 	function updateTextLength() {
 		this.labels.item(0).children.item(0).textContent = this.value.length + ' / ' + this.maxLength;
 	}

+ 1 - 0
i18n/en.json

@@ -590,6 +590,7 @@
         "special": "Content of this special page:"
     },
     "settings": {
+        "button": "Try the Dashboard",
         "channel current": "these are the current settings for this channel:",
         "channel lang": "the language for this channel is:",
         "channel langchanged": "you changed the language for this channel to:",