Преглед на файлове

Multiple fixes
Not waiting for iOS because @Friskygote can fix restart himself

Markus-Rost преди 3 години
родител
ревизия
39d76ea883
променени са 19 файла, в които са добавени 84 реда и са изтрити 48 реда
  1. 3 3
      README.md
  2. 3 1
      bot.js
  3. 1 1
      cmds/invite.js
  4. 2 3
      cmds/patreon.js
  5. 1 1
      cmds/stop.js
  6. 7 5
      cmds/wiki/diff.js
  7. 15 8
      cmds/wiki/overview.js
  8. 5 4
      cmds/wiki/user.js
  9. 2 2
      dashboard/guilds.js
  10. 1 1
      dashboard/index.html
  11. 2 2
      dashboard/login.html
  12. 3 0
      dashboard/src/index.css
  13. 1 1
      dashboard/util.js
  14. 4 1
      functions/phabricator.js
  15. 20 11
      functions/special_page.js
  16. 2 2
      functions/verify.js
  17. 1 0
      i18n/en.json
  18. 10 2
      main.js
  19. 1 0
      util/default.json

+ 3 - 3
README.md

@@ -1,11 +1,11 @@
 # Wiki-Bot[<img src="https://translate.wikibot.de/widgets/wiki-bot/-/svg-badge.svg" alt="Translation status" align="right" />](#translations)[<img src="https://github.com/Markus-Rost/discord-wiki-bot/workflows/Node.js CI/badge.svg" alt="Node.js CI" align="right" />](https://github.com/Markus-Rost/discord-wiki-bot/actions)
 # Wiki-Bot[<img src="https://translate.wikibot.de/widgets/wiki-bot/-/svg-badge.svg" alt="Translation status" align="right" />](#translations)[<img src="https://github.com/Markus-Rost/discord-wiki-bot/workflows/Node.js CI/badge.svg" alt="Node.js CI" align="right" />](https://github.com/Markus-Rost/discord-wiki-bot/actions)
-[<img src="/dashboard/src/icon.png" alt="Wiki-Bot" align="right" />](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands)
+[<img src="/dashboard/src/icon.png" alt="Wiki-Bot" align="right" />](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands)
 
 
 **Wiki-Bot** is a bot for [Discord](https://discord.com/) with the purpose to easily link and search [MediaWiki](https://www.mediawiki.org/wiki/MediaWiki) sites like [Wikipedia](https://www.wikipedia.org/) and [Fandom](https://www.fandom.com/) wikis. **Wiki-Bot** shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.
 **Wiki-Bot** is a bot for [Discord](https://discord.com/) with the purpose to easily link and search [MediaWiki](https://www.mediawiki.org/wiki/MediaWiki) sites like [Wikipedia](https://www.wikipedia.org/) and [Fandom](https://www.fandom.com/) wikis. **Wiki-Bot** shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.
 
 
 **Wiki-Bot** has translations for Bengali, German, English, Spanish, French, Hindi, Korean, Dutch, Polish, Brazilian Portuguese, Russian, Turkish, Simplified Chinese and Traditional Chinese.
 **Wiki-Bot** has translations for Bengali, German, English, Spanish, French, Hindi, Korean, Dutch, Polish, Brazilian Portuguese, Russian, Turkish, Simplified Chinese and Traditional Chinese.
 
 
-[Use this link to invite **Wiki-Bot** to your Discord server.](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands)
+[Use this link to invite **Wiki-Bot** to your Discord server.](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands)
 
 
 [Change the server settings for **Wiki-Bot** using the dashboard.](https://settings.wikibot.de/)
 [Change the server settings for **Wiki-Bot** using the dashboard.](https://settings.wikibot.de/)
 
 
@@ -20,7 +20,7 @@ Support server: [https://discord.gg/v77RTk5](https://discord.gg/v77RTk5)
 * [Voice Channel](#voice-channel)
 * [Voice Channel](#voice-channel)
 
 
 ## Setup
 ## Setup
-After [inviting](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands) **Wiki-Bot** to your server you need to set the wiki you want to search by default. You do this with the `!wiki settings` command.
+After [inviting](https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands) **Wiki-Bot** to your server you need to set the wiki you want to search by default. You do this with the `!wiki settings` command.
 * Change the wiki with `!wiki settings wiki <url>`
 * Change the wiki with `!wiki settings wiki <url>`
   * Example: `!wiki settings wiki https://minecraft.fandom.com/wiki/Minecraft_Wiki`
   * Example: `!wiki settings wiki https://minecraft.fandom.com/wiki/Minecraft_Wiki`
 * Change the language with `!wiki settings lang <language>`
 * Change the language with `!wiki settings lang <language>`

+ 3 - 1
bot.js

@@ -476,7 +476,9 @@ const common_warnings = {
 		'Unrecognized values for parameter "prop": pageimages, extracts.',
 		'Unrecognized values for parameter "prop": pageimages, extracts.',
 		'Unrecognized values for parameter "prop": pageimages, extracts',
 		'Unrecognized values for parameter "prop": pageimages, extracts',
 		'Unrecognized value for parameter "prop": extracts.',
 		'Unrecognized value for parameter "prop": extracts.',
-		'Unrecognized value for parameter "prop": pageimages.'
+		'Unrecognized value for parameter "prop": extracts',
+		'Unrecognized value for parameter "prop": pageimages.',
+		'Unrecognized value for parameter "prop": pageimages'
 	]
 	]
 }
 }
 
 

+ 1 - 1
cmds/invite.js

@@ -15,7 +15,7 @@ function cmd_invite(lang, msg, args, line, wiki) {
 		msg.client.generateInvite({
 		msg.client.generateInvite({
 			permissions: defaultPermissions
 			permissions: defaultPermissions
 		}).then( invite => {
 		}).then( invite => {
-			msg.sendChannel( lang.get('invite.bot') + '\n<' + invite + '%20applications.commands' + '>' );
+			msg.sendChannel( lang.get('invite.bot') + '\n<' + invite + '+applications.commands' + '>' );
 		}, log_error );
 		}, log_error );
 	}
 	}
 }
 }

+ 2 - 3
cmds/patreon.js

@@ -18,10 +18,9 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 	
 	
 	if ( args[0] === 'enable' && /^\d+$/.test(args.slice(1).join(' ')) ) return msg.client.shard.broadcastEval( `this.guilds.cache.get('${args[1]}')?.name`, shardIDForGuildID(args[1], msg.client.shard.count) ).then( guild => {
 	if ( args[0] === 'enable' && /^\d+$/.test(args.slice(1).join(' ')) ) return msg.client.shard.broadcastEval( `this.guilds.cache.get('${args[1]}')?.name`, shardIDForGuildID(args[1], msg.client.shard.count) ).then( guild => {
 		if ( !guild ) return msg.client.generateInvite({
 		if ( !guild ) return msg.client.generateInvite({
-			permissions: defaultPermissions,
-			guild: args[1]
+			permissions: defaultPermissions
 		}).then( invite => {
 		}).then( invite => {
-			msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.\n<' + invite + '%20applications.commands' + '>', {}, true )
+			msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.\n<' + invite + '+applications.commands&guild_id=' + args[1] + '&disable_guild_select=true' + '>', {}, true )
 		}, log_error );
 		}, log_error );
 		if ( patreons[args[1]] ) return msg.replyMsg( '"' + guild + '" has the patreon features already enabled.', {}, true );
 		if ( patreons[args[1]] ) return msg.replyMsg( '"' + guild + '" has the patreon features already enabled.', {}, true );
 		db.query( 'SELECT count, COUNT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = $1 GROUP BY patreons.patreon', [msg.author.id] ).then( ({rows:[row]}) => {
 		db.query( 'SELECT count, COUNT(guild) guilds FROM patreons LEFT JOIN discord ON discord.patreon = patreons.patreon WHERE patreons.patreon = $1 GROUP BY patreons.patreon', [msg.author.id] ).then( ({rows:[row]}) => {

+ 1 - 1
cmds/stop.js

@@ -14,7 +14,7 @@ async function cmd_stop(lang, msg, args, line, wiki) {
 	} else if ( args.join(' ').split('\n')[0].isMention(msg.guild) ) {
 	} else if ( args.join(' ').split('\n')[0].isMention(msg.guild) ) {
 		await msg.replyMsg( 'I\'ll restart myself now!', {}, true );
 		await msg.replyMsg( 'I\'ll restart myself now!', {}, true );
 		console.log( '\n- Restarting all shards!\n\n' );
 		console.log( '\n- Restarting all shards!\n\n' );
-		await msg.client.shard.respawnAll();
+		await msg.client.shard.respawnAll(5000, 500, -1);
 	} else if ( !msg.channel.isGuild() || !pause[msg.guild.id] ) {
 	} else if ( !msg.channel.isGuild() || !pause[msg.guild.id] ) {
 		this.LINK(lang, msg, line, wiki);
 		this.LINK(lang, msg, line, wiki);
 	}
 	}

+ 7 - 5
cmds/wiki/diff.js

@@ -192,18 +192,18 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, compare)
 					timeZone: 'UTC'
 					timeZone: 'UTC'
 				}, timeoptions));
 				}, timeoptions));
 			}
 			}
-			var timestamp = [lang.get('diff.info.timestamp'), dateformat.format(new Date(revisions[0].timestamp))];
+			var editDate = new Date(revisions[0].timestamp);
+			var timestamp = [lang.get('diff.info.timestamp'), dateformat.format(editDate), '<t:' + Math.trunc(editDate.getTime() / 1000) + ':R>'];
 			var difference = revisions[0].size - ( revisions[1] ? revisions[1].size : 0 );
 			var difference = revisions[0].size - ( revisions[1] ? revisions[1].size : 0 );
 			var size = [lang.get('diff.info.size'), lang.get('diff.info.bytes', ( difference > 0 ? '+' : '' ) + difference.toLocaleString(lang.get('dateformat')), difference, ( revisions[0].minor !== undefined ? lang.get('diff.info.minor') : '' ))];
 			var size = [lang.get('diff.info.size'), lang.get('diff.info.bytes', ( difference > 0 ? '+' : '' ) + difference.toLocaleString(lang.get('dateformat')), difference, ( revisions[0].minor !== undefined ? lang.get('diff.info.minor') : '' ))];
 			var comment = [lang.get('diff.info.comment'), ( revisions[0].commenthidden !== undefined ? lang.get('diff.hidden') : ( revisions[0].parsedcomment ? ( msg.showEmbed() ? htmlToDiscord(revisions[0].parsedcomment, wiki.toLink(title), true) : htmlToPlain(revisions[0].parsedcomment) ) : lang.get('diff.nocomment') ) )];
 			var comment = [lang.get('diff.info.comment'), ( revisions[0].commenthidden !== undefined ? lang.get('diff.hidden') : ( revisions[0].parsedcomment ? ( msg.showEmbed() ? htmlToDiscord(revisions[0].parsedcomment, wiki.toLink(title), true) : htmlToPlain(revisions[0].parsedcomment) ) : lang.get('diff.nocomment') ) )];
-			if ( revisions[0].tags.length ) var tags = [lang.get('diff.info.tags'), body.query.tags.filter( tag => revisions[0].tags.includes( tag.name ) ).map( tag => tag.displayname ).join(', ')];
+			if ( revisions[0].tags.length ) var tags = [lang.get('diff.info.tags'), body.query.tags.filter( tag => tag.displayname && revisions[0].tags.includes( tag.name ) ).map( tag => tag.displayname || tag.name ).join(', ')];
 			
 			
 			var pagelink = wiki.toLink(title, {diff,oldid});
 			var pagelink = wiki.toLink(title, {diff,oldid});
 			var text = '<' + pagelink + '>';
 			var text = '<' + pagelink + '>';
 			var embed = null;
 			var embed = null;
 			if ( msg.showEmbed() ) {
 			if ( msg.showEmbed() ) {
-				embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting( title + '?diff=' + diff + '&oldid=' + oldid ) ).setURL( pagelink ).addField( editor[0], editor[1], true ).addField( size[0], size[1], true ).addField( comment[0], comment[1] ).setFooter( timestamp[1] );
-				if ( tags ) embed.addField( tags[0], htmlToDiscord(tags[1], pagelink) );
+				embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting( title + '?diff=' + diff + '&oldid=' + oldid ) ).setURL( pagelink ).addField( editor[0], editor[1], true ).addField( size[0], size[1], true ).addField( timestamp[0], timestamp[1] + '\n' + timestamp[2], true ).addField( comment[0], comment[1] ).setTimestamp( editDate );
 				
 				
 				var more = '\n__' + lang.get('diff.info.more') + '__';
 				var more = '\n__' + lang.get('diff.info.more') + '__';
 				var whitespace = '__' + lang.get('diff.info.whitespace') + '__';
 				var whitespace = '__' + lang.get('diff.info.whitespace') + '__';
@@ -244,6 +244,7 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, compare)
 				}, error => {
 				}, error => {
 					console.log( '- Error while getting the diff: ' + error );
 					console.log( '- Error while getting the diff: ' + error );
 				} ).finally( () => {
 				} ).finally( () => {
+					if ( tags?.[1] ) embed.addField( tags[0], htmlToDiscord(tags[1], pagelink) );
 					msg.sendChannel( spoiler + text + spoiler, {embed} );
 					msg.sendChannel( spoiler + text + spoiler, {embed} );
 					
 					
 					if ( reaction ) reaction.removeEmoji();
 					if ( reaction ) reaction.removeEmoji();
@@ -264,6 +265,7 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, compare)
 							embed.addField( lang.get('diff.info.added'), content, true );
 							embed.addField( lang.get('diff.info.added'), content, true );
 						} else embed.addField( lang.get('diff.info.added'), whitespace, true );
 						} else embed.addField( lang.get('diff.info.added'), whitespace, true );
 					}
 					}
+					if ( tags?.[1] ) embed.addField( tags[0], htmlToDiscord(tags[1], pagelink) );
 					
 					
 					msg.sendChannel( spoiler + text + spoiler, {embed} );
 					msg.sendChannel( spoiler + text + spoiler, {embed} );
 					
 					
@@ -272,7 +274,7 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, compare)
 			}
 			}
 			else {
 			else {
 				text += '\n\n' + editor.join(' ') + '\n' + timestamp.join(' ') + '\n' + size.join(' ') + '\n' + comment.join(' ');
 				text += '\n\n' + editor.join(' ') + '\n' + timestamp.join(' ') + '\n' + size.join(' ') + '\n' + comment.join(' ');
-				if ( tags ) text += htmlToDiscord( '\n' + tags.join(' ') );
+				if ( tags?.[1] ) text += htmlToDiscord( '\n' + tags.join(' ') );
 				
 				
 				msg.sendChannel( spoiler + text + spoiler, {embed} );
 				msg.sendChannel( spoiler + text + spoiler, {embed} );
 				
 				

+ 15 - 8
cmds/wiki/overview.js

@@ -12,7 +12,7 @@ const {toFormatting, toPlaintext, escapeFormatting} = require('../../util/functi
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {String} spoiler - If the response is in a spoiler.
  */
  */
 function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
-	got.get( wiki + 'api.php?action=query&meta=siteinfo' + ( wiki.isFandom() ? '|allmessages&ammessages=custom-GamepediaNotice|custom-FandomMergeNotice&amenableparser=true' : '' ) + '&siprop=general|statistics|languages|rightsinfo' + ( wiki.isFandom() ? '|variables' : '' ) + '&siinlanguagecode=' + lang.lang + '&list=logevents&ledir=newer&lelimit=1&leprop=timestamp&titles=Special:Statistics&format=json' ).then( response => {
+	got.get( wiki + 'api.php?uselang=' + lang.lang + '&action=query&meta=allmessages|siteinfo&amenableparser=true&amtitle=Special:Statistics&ammessages=statistics' + ( wiki.isFandom() ? '|custom-GamepediaNotice|custom-FandomMergeNotice' : '' ) + '&siprop=general|statistics|languages|rightsinfo' + ( wiki.isFandom() ? '|variables' : '' ) + '&siinlanguagecode=' + lang.lang + '&list=logevents&ledir=newer&lelimit=1&leprop=timestamp&titles=Special:Statistics&format=json' ).then( response => {
 		var body = response.body;
 		var body = response.body;
 		if ( body && body.warnings ) log_warn(body.warnings);
 		if ( body && body.warnings ) log_warn(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.pages ) {
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.pages ) {
@@ -32,7 +32,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 		logging(wiki, msg.guild?.id, 'overview');
 		logging(wiki, msg.guild?.id, 'overview');
 		var version = [lang.get('overview.version'), body.query.general.generator];
 		var version = [lang.get('overview.version'), body.query.general.generator];
 		var creation_date = null;
 		var creation_date = null;
-		var created = [lang.get('overview.created'), lang.get('overview.unknown')];
+		var created = [lang.get('overview.created'), lang.get('overview.unknown'), ''];
 		try {
 		try {
 			var dateformat = new Intl.DateTimeFormat(lang.get('dateformat'), Object.assign({
 			var dateformat = new Intl.DateTimeFormat(lang.get('dateformat'), Object.assign({
 				timeZone: body.query.general.timezone
 				timeZone: body.query.general.timezone
@@ -46,6 +46,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 		if ( body.query.logevents?.[0]?.timestamp ) {
 		if ( body.query.logevents?.[0]?.timestamp ) {
 			creation_date = new Date(body.query.logevents[0].timestamp);
 			creation_date = new Date(body.query.logevents[0].timestamp);
 			created[1] = dateformat.format(creation_date);
 			created[1] = dateformat.format(creation_date);
+			created[2] = '<t:' + Math.trunc(creation_date.getTime() / 1000) + ':R>';
 		}
 		}
 		var language = [lang.get('overview.lang'), body.query.languages.find( language => {
 		var language = [lang.get('overview.lang'), body.query.languages.find( language => {
 			return language.code === body.query.general.lang;
 			return language.code === body.query.general.lang;
@@ -90,6 +91,11 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 		var embed = null;
 		var embed = null;
 		if ( msg.showEmbed() ) {
 		if ( msg.showEmbed() ) {
 			embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(title) ).setURL( pagelink ).setThumbnail( new URL(body.query.general.logo, wiki).href );
 			embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(title) ).setURL( pagelink ).setThumbnail( new URL(body.query.general.logo, wiki).href );
+			if ( body.query.allmessages?.[0]?.['*']?.trim?.() ) {
+				let displaytitle = escapeFormatting(body.query.allmessages[0]['*'].trim());
+				if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
+				embed.setTitle( displaytitle );
+			}
 		}
 		}
 		else {
 		else {
 			text += '\n';
 			text += '\n';
@@ -106,11 +112,11 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 			var manager = [lang.get('overview.manager'), ''];
 			var manager = [lang.get('overview.manager'), ''];
 			var founder = [lang.get('overview.founder')];
 			var founder = [lang.get('overview.founder')];
 			var crossover = [lang.get('overview.crossover')];
 			var crossover = [lang.get('overview.crossover')];
-			if ( body.query.allmessages?.[0]?.['*'] ) {
-				crossover[1] = '<https://' + body.query.allmessages[0]['*'] + '.gamepedia.com/>';
+			if ( body.query.allmessages?.[1]?.['*']?.trim?.() ) {
+				crossover[1] = '<https://' + body.query.allmessages[1]['*'].trim() + '.gamepedia.com/>';
 			}
 			}
-			if ( body.query.allmessages?.[1]?.['*'] ) {
-				let mergeNotice = body.query.allmessages[1]['*'];
+			if ( body.query.allmessages?.[2]?.['*']?.trim?.() ) {
+				let mergeNotice = body.query.allmessages[2]['*'].trim();
 				if ( !mergeNotice.includes( '|' ) ) {
 				if ( !mergeNotice.includes( '|' ) ) {
 					mergeNotice = mergeNotice.split('/');
 					mergeNotice = mergeNotice.split('/');
 					crossover[1] = '<https://' + mergeNotice[0] + '.fandom.com/' + ( mergeNotice[1] ? '/' + mergeNotice[1] : '' ) + '>';
 					crossover[1] = '<https://' + mergeNotice[0] + '.fandom.com/' + ( mergeNotice[1] ? '/' + mergeNotice[1] : '' ) + '>';
@@ -132,6 +138,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 				if ( site.creation_date && creation_date > new Date(site.creation_date) ) {
 				if ( site.creation_date && creation_date > new Date(site.creation_date) ) {
 					creation_date = new Date(site.creation_date);
 					creation_date = new Date(site.creation_date);
 					created[1] = dateformat.format(creation_date);
 					created[1] = dateformat.format(creation_date);
+					created[2] = '<t:' + Math.trunc(creation_date.getTime() / 1000) + ':R>';
 				}
 				}
 				if ( site.desc ) {
 				if ( site.desc ) {
 					description[1] = escapeFormatting(site.desc);
 					description[1] = escapeFormatting(site.desc);
@@ -189,7 +196,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 					if ( official[1] ) embed.addField( official[0], official[1], true );
 					if ( official[1] ) embed.addField( official[0], official[1], true );
 					embed.addField( version[0], version[1], true ).addField( language[0], language[1], true );
 					embed.addField( version[0], version[1], true ).addField( language[0], language[1], true );
 					if ( rtl[1] ) embed.addField( rtl[0], rtl[1], true );
 					if ( rtl[1] ) embed.addField( rtl[0], rtl[1], true );
-					embed.addField( created[0], created[1], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true );
+					embed.addField( created[0], created[1] + '\n' + created[2], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true );
 					if ( posts[1] ) embed.addField( posts[0], posts[1], true );
 					if ( posts[1] ) embed.addField( posts[0], posts[1], true );
 					if ( walls[1] ) embed.addField( walls[0], walls[1], true );
 					if ( walls[1] ) embed.addField( walls[0], walls[1], true );
 					if ( comments[1] ) embed.addField( comments[0], comments[1], true );
 					if ( comments[1] ) embed.addField( comments[0], comments[1], true );
@@ -234,7 +241,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 		if ( msg.showEmbed() ) {
 		if ( msg.showEmbed() ) {
 			embed.addField( version[0], version[1], true ).addField( language[0], language[1], true );
 			embed.addField( version[0], version[1], true ).addField( language[0], language[1], true );
 			if ( rtl[1] ) embed.addField( rtl[0], rtl[1], true );
 			if ( rtl[1] ) embed.addField( rtl[0], rtl[1], true );
-			embed.addField( created[0], created[1], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true ).addField( users[0], users[1], true ).addField( admins[0], admins[1], true ).addField( license[0], license[1], true ).addField( misermode[0], misermode[1], true ).setFooter( lang.get('overview.inaccurate') );
+			embed.addField( created[0], created[1] + '\n' + created[2], true ).addField( articles[0], articles[1], true ).addField( pages[0], pages[1], true ).addField( edits[0], edits[1], true ).addField( users[0], users[1], true ).addField( admins[0], admins[1], true ).addField( license[0], license[1], true ).addField( misermode[0], misermode[1], true ).setFooter( lang.get('overview.inaccurate') );
 			if ( readonly[1] ) embed.addField( readonly[0], readonly[1] );
 			if ( readonly[1] ) embed.addField( readonly[0], readonly[1] );
 		}
 		}
 		else {
 		else {

+ 5 - 4
cmds/wiki/user.js

@@ -306,7 +306,8 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				timeZone: 'UTC'
 				timeZone: 'UTC'
 			}, timeoptions));
 			}, timeoptions));
 		}
 		}
-		var registration = [lang.get('user.info.registration'), dateformat.format(new Date(queryuser.registration))];
+		let registrationDate = new Date(queryuser.registration);
+		var registration = [lang.get('user.info.registration'), dateformat.format(registrationDate), '<t:' + Math.trunc(registrationDate.getTime() / 1000) + ':R>'];
 		var editcount = [lang.get('user.info.editcount'), queryuser.editcount.toLocaleString(lang.get('dateformat'))];
 		var editcount = [lang.get('user.info.editcount'), queryuser.editcount.toLocaleString(lang.get('dateformat'))];
 		var groups = queryuser.groups.filter( group => !usergroups.ignored.includes( group ) );
 		var groups = queryuser.groups.filter( group => !usergroups.ignored.includes( group ) );
 		var globalgroups = [];
 		var globalgroups = [];
@@ -334,7 +335,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 			console.log( '- Error while getting the group names: ' + error );
 			console.log( '- Error while getting the group names: ' + error );
 		} ).finally( () => {
 		} ).finally( () => {
 			var group = [lang.get('user.info.group', ( groups.filter( usergroup => {
 			var group = [lang.get('user.info.group', ( groups.filter( usergroup => {
-				return !['autoconfirmed', 'user'].includes( usergroup )
+				return !['autoconfirmed', 'emailconfirmed', 'user'].includes( usergroup )
 			} ).length || 1 ))];
 			} ).length || 1 ))];
 			for ( var i = 0; i < usergroups.sorted.length; i++ ) {
 			for ( var i = 0; i < usergroups.sorted.length; i++ ) {
 				let usergroup = usergroups.sorted[i];
 				let usergroup = usergroups.sorted[i];
@@ -345,7 +346,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 						return groupnames[groups.indexOf(customgroup)];
 						return groupnames[groups.indexOf(customgroup)];
 					} ));
 					} ));
 				}
 				}
-				else if ( groups.includes( usergroup ) && ( group.length === 1 || !['autoconfirmed', 'user'].includes( usergroup ) ) ) {
+				else if ( groups.includes( usergroup ) && ( group.length === 1 || !['autoconfirmed', 'emailconfirmed', 'user'].includes( usergroup ) ) ) {
 					group.push(groupnames[groups.indexOf(usergroup)]);
 					group.push(groupnames[groups.indexOf(usergroup)]);
 				}
 				}
 			}
 			}
@@ -445,7 +446,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				if ( globalgroup.length > 1 ) {
 				if ( globalgroup.length > 1 ) {
 					embed.addField( globalgroup[0], globalgroup.slice(1).join(',\n'), true );
 					embed.addField( globalgroup[0], globalgroup.slice(1).join(',\n'), true );
 				}
 				}
-				embed.addField( gender[0], gender[1], true ).addField( registration[0], registration[1], true );
+				embed.addField( gender[0], gender[1], true ).addField( registration[0], registration[1] + '\n' + registration[2], true );
 				
 				
 				if ( querypage.pageprops && querypage.pageprops.description ) {
 				if ( querypage.pageprops && querypage.pageprops.description ) {
 					var description = htmlToDiscord( querypage.pageprops.description );
 					var description = htmlToDiscord( querypage.pageprops.description );

+ 2 - 2
dashboard/guilds.js

@@ -123,8 +123,8 @@ function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action
 		res.setHeader('Set-Cookie', [`guild="${guild.id}/settings"; SameSite=Lax; Path=/`]);
 		res.setHeader('Set-Cookie', [`guild="${guild.id}/settings"; SameSite=Lax; Path=/`]);
 		let url = oauth.generateAuthUrl( {
 		let url = oauth.generateAuthUrl( {
 			scope: ['identify', 'guilds', 'bot', 'applications.commands'],
 			scope: ['identify', 'guilds', 'bot', 'applications.commands'],
-			permissions: defaultPermissions,
-			guildId: guild.id, state: userSession.state
+			permissions: defaultPermissions, guildId: guild.id,
+			disableGuildSelect: true, state: userSession.state
 		} );
 		} );
 		$('#channellist').empty();
 		$('#channellist').empty();
 		$('<a class="channel channel-header">').attr('href', url).append(
 		$('<a class="channel channel-header">').attr('href', url).append(

+ 1 - 1
dashboard/index.html

@@ -55,7 +55,7 @@
 		<div id="guildlist">
 		<div id="guildlist">
 			<div class="guild" id="invite">
 			<div class="guild" id="invite">
 				<div class="bar"></div>
 				<div class="bar"></div>
-				<a href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands" alt="Invite Wiki-Bot">
+				<a href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands" alt="Invite Wiki-Bot">
 					<div class="avatar svg-avatar">
 					<div class="avatar svg-avatar">
 						<svg width="24" height="24" viewBox="0 0 24 24">
 						<svg width="24" height="24" viewBox="0 0 24 24">
 							<path fill="currentColor" d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"></path>
 							<path fill="currentColor" d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"></path>

+ 2 - 2
dashboard/login.html

@@ -41,7 +41,7 @@
 				<img src="/src/settings.svg" alt="Settings">
 				<img src="/src/settings.svg" alt="Settings">
 				<div>Login</div>
 				<div>Login</div>
 			</a>
 			</a>
-			<a class="channel" id="invite-wikibot" href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands" title="Invite Wiki-Bot">
+			<a class="channel" id="invite-wikibot" href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands" title="Invite Wiki-Bot">
 				<img src="/src/channel.svg" alt="Channel">
 				<img src="/src/channel.svg" alt="Channel">
 				<div>Invite Wiki-Bot</div>
 				<div>Invite Wiki-Bot</div>
 			</a>
 			</a>
@@ -57,7 +57,7 @@
 		<div id="guildlist">
 		<div id="guildlist">
 			<div class="guild" id="invite">
 			<div class="guild" id="invite">
 				<div class="bar"></div>
 				<div class="bar"></div>
-				<a href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot%20applications.commands" alt="Invite Wiki-Bot">
+				<a href="https://discord.com/oauth2/authorize?client_id=461189216198590464&permissions=939904064&scope=bot+applications.commands" alt="Invite Wiki-Bot">
 					<div class="avatar svg-avatar">
 					<div class="avatar svg-avatar">
 						<svg width="24" height="24" viewBox="0 0 24 24">
 						<svg width="24" height="24" viewBox="0 0 24 24">
 							<path fill="currentColor" d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"></path>
 							<path fill="currentColor" d="M20 11.1111H12.8889V4H11.1111V11.1111H4V12.8889H11.1111V20H12.8889V12.8889H20V11.1111Z"></path>

+ 3 - 0
dashboard/src/index.css

@@ -589,6 +589,9 @@ fieldset textarea {
 	height: 128px;
 	height: 128px;
 	background: #32353b;
 	background: #32353b;
 }
 }
+.theme-light #wb-settings-avatar-preview-img {
+	background-color: #fafafa;
+}
 .wb-settings-display:first-of-type,
 .wb-settings-display:first-of-type,
 .wb-settings-permission:first-of-type {
 .wb-settings-permission:first-of-type {
 	display: inline-block;
 	display: inline-block;

+ 1 - 1
dashboard/util.js

@@ -290,7 +290,7 @@ function createNotice($, notice, dashboardLang, args = []) {
 			type = 'error';
 			type = 'error';
 			title.text(dashboardLang.get('notice.noslash.title'));
 			title.text(dashboardLang.get('notice.noslash.title'));
 			text.text(dashboardLang.get('notice.noslash.text'));
 			text.text(dashboardLang.get('notice.noslash.text'));
-			note = $('<a target="_blank">').text(dashboardLang.get('notice.noslash.note')).attr('href', `https://discord.com/api/oauth2/authorize?client_id=${process.env.bot}&scope=applications.commands&guild_id=${args[0]}`);
+			note = $('<a target="_blank">').text(dashboardLang.get('notice.noslash.note')).attr('href', `https://discord.com/api/oauth2/authorize?client_id=${process.env.bot}&scope=applications.commands&guild_id=${args[0]}&disable_guild_select=true`);
 			break;
 			break;
 		case 'wikiblocked':
 		case 'wikiblocked':
 			type = 'error';
 			type = 'error';

+ 4 - 1
functions/phabricator.js

@@ -80,6 +80,7 @@ function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '') {
 					var content = parse_text( comment.comments[0].content.raw, site );
 					var content = parse_text( comment.comments[0].content.raw, site );
 					if ( content.length > 1000 ) content = limitLength(content, 1000, 20);
 					if ( content.length > 1000 ) content = limitLength(content, 1000, 20);
 					embed.spliceFields( 0, 0, {name: 'Comment', value: content} );
 					embed.spliceFields( 0, 0, {name: 'Comment', value: content} );
+					if ( embed.description.length > 500 ) embed.setDescription( limitLength(description, 500, 250) );
 				}
 				}
 			}, error => {
 			}, error => {
 				console.log( '- Error while getting the task transactions: ' + error );
 				console.log( '- Error while getting the task transactions: ' + error );
@@ -105,9 +106,11 @@ function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '') {
  */
  */
 function parse_text(text, site) {
 function parse_text(text, site) {
 	text = text.replace( /```lang=/g, '```' );
 	text = text.replace( /```lang=/g, '```' );
+	text = text.replace( /^>>! (.+?)$/gm, '> *$1*' );
+	text = text.replace( /^>>/gm, '> >' );
 	text = text.replace( /##(.+?)##/g, '`$1`' );
 	text = text.replace( /##(.+?)##/g, '`$1`' );
 	text = text.replace( /!!(.+?)!!/g, '`$1`' );
 	text = text.replace( /!!(.+?)!!/g, '`$1`' );
-	text = text.replace( /\/\/(.+?)\/\//g, '*$1*' );
+	text = text.replace( /(?<!https?:)\/\/(.+?)(?<!https?:)\/\//g, '*$1*' );
 	text = text.replace( /\[\[ ?(.+?) ?(?:\| ?(.+?) ?)?\]\]/g, (match, target, display) => {
 	text = text.replace( /\[\[ ?(.+?) ?(?:\| ?(.+?) ?)?\]\]/g, (match, target, display) => {
 		var link = target;
 		var link = target;
 		if ( /^(?:(?:https?:)?\/\/|\/|#)/.test(target) ) link = new URL(target, site).href;
 		if ( /^(?:(?:https?:)?\/\/|\/|#)/.test(target) ) link = new URL(target, site).href;

+ 20 - 11
functions/special_page.js

@@ -42,7 +42,8 @@ const queryfunctions = {
 				timeZone: 'UTC'
 				timeZone: 'UTC'
 			}, timeoptions));
 			}, timeoptions));
 		}
 		}
-		return dateformat.format(new Date(result.timestamp)) + ': [' + escapeFormatting(result.title) + '](' + wiki.toLink(result.title, '', '', true) + ')';
+		let lastEditDate = new Date(result.timestamp);
+		return dateformat.format(lastEditDate) + ' <t:' + Math.trunc(lastEditDate.getTime() / 1000) + ':R>: [' + escapeFormatting(result.title) + '](' + wiki.toLink(result.title, '', '', true) + ')';
 	} ).join('\n'),
 	} ).join('\n'),
 	media: (query, wiki, lang) => query.querypage.results.map( result => {
 	media: (query, wiki, lang) => query.querypage.results.map( result => {
 		var ms = result.title.split(';');
 		var ms = result.title.split(';');
@@ -107,6 +108,7 @@ const querypages = {
 }
 }
 
 
 const descriptions = {
 const descriptions = {
+	block: 'blockiptext&amargs=16|19',
 	checkuser: 'checkuser-summary&amargs=16|19',
 	checkuser: 'checkuser-summary&amargs=16|19',
 	resettokens: 'resettokens-text',
 	resettokens: 'resettokens-text',
 	allmessages: 'allmessagestext',
 	allmessages: 'allmessagestext',
@@ -144,21 +146,28 @@ function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, embe
 	if ( specialpage === 'recentchanges' && msg.isAdmin() ) {
 	if ( specialpage === 'recentchanges' && msg.isAdmin() ) {
 		embed.addField( lang.get('rcscript.title'), lang.get('rcscript.ad', ( patreons[msg?.guild?.id] || process.env.prefix ), '[RcGcDw](https://gitlab.com/piotrex43/RcGcDw)') );
 		embed.addField( lang.get('rcscript.title'), lang.get('rcscript.ad', ( patreons[msg?.guild?.id] || process.env.prefix ), '[RcGcDw](https://gitlab.com/piotrex43/RcGcDw)') );
 	}
 	}
-	got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&meta=allmessages|siteinfo&siprop=general&amenableparser=true&amtitle=' + encodeURIComponent( title ) + '&ammessages=' + ( descriptions.hasOwnProperty(specialpage) ? descriptions[specialpage] : encodeURIComponent( specialpage ) + '-summary' ) + ( querypages.hasOwnProperty(specialpage) ? querypages[specialpage][0] : '' ) + '&format=json' ).then( response => {
+	got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&meta=allmessages|siteinfo&siprop=general&amenableparser=true&amtitle=' + encodeURIComponent( title ) + '&ammessages=' + encodeURIComponent( specialpage ) + '|' + ( descriptions.hasOwnProperty(specialpage) ? descriptions[specialpage] : encodeURIComponent( specialpage ) + '-summary' ) + ( querypages.hasOwnProperty(specialpage) ? querypages[specialpage][0] : '' ) + '&format=json' ).then( response => {
 		var body = response.body;
 		var body = response.body;
 		if ( body && body.warnings ) log_warn(body.warnings);
 		if ( body && body.warnings ) log_warn(body.warnings);
 		if ( response.statusCode !== 200 || body?.batchcomplete === undefined ) {
 		if ( response.statusCode !== 200 || body?.batchcomplete === undefined ) {
 			console.log( '- ' + response.statusCode + ': Error while getting the special page: ' + ( body && body.error && body.error.info ) );
 			console.log( '- ' + response.statusCode + ': Error while getting the special page: ' + ( body && body.error && body.error.info ) );
+			return;
 		}
 		}
-		else {
-			if ( body.query.allmessages[0]['*'] ) {
-				var description = toMarkdown(body.query.allmessages[0]['*'], wiki, title, true);
-				if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
-				embed.setDescription( description );
-			}
-			if ( msg.channel.isGuild() && patreons[msg.guild?.id] && querypages.hasOwnProperty(specialpage) ) {
-				var text = Util.splitMessage( querypages[specialpage][1](body.query, wiki, lang), {maxLength:1000} )[0];
-				embed.addField( lang.get('search.special'), ( text || lang.get('search.empty') ) );
+		if ( body.query.allmessages?.[0]?.['*']?.trim?.() ) {
+			let displaytitle = escapeFormatting(body.query.allmessages[0]['*'].trim());
+			if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
+			embed.setTitle( displaytitle );
+		}
+		if ( body.query.allmessages?.[1]?.['*']?.trim?.() ) {
+			var description = toMarkdown(body.query.allmessages[1]['*'], wiki, title, true);
+			if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
+			embed.setDescription( description );
+		}
+		if ( msg.channel.isGuild() && patreons[msg.guild?.id] && querypages.hasOwnProperty(specialpage) ) {
+			var text = Util.splitMessage( querypages[specialpage][1](body.query, wiki, lang), {maxLength:1000} )[0];
+			embed.addField( lang.get('search.special'), ( text || lang.get('search.empty') ) );
+			if ( body.query.querypage.cached !== undefined ) {
+				embed.setFooter( lang.get('search.cached') ).setTimestamp(new Date(body.query.querypage.cachedtimestamp));
 			}
 			}
 		}
 		}
 	}, error => {
 	}, error => {

+ 2 - 2
functions/verify.js

@@ -189,8 +189,8 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 						}
 						}
 					}
 					}
 					var help_link = '';
 					var help_link = '';
-					if ( wiki.isGamepedia() ) help_link = lang.get('verify.help_gamepedia') + '?c=' + ( patreons[channel.guild.id] && patreons[channel.guild.id] !== process.env.prefix ? encodeURIComponent( patreons[channel.guild.id] + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + toTitle(username) + '&discord=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator;
-					else if ( wiki.isFandom() ) help_link = lang.get('verify.help_fandom') + '/' + toTitle(username) + '?c=' + ( patreons[channel.guild.id] && patreons[channel.guild.id] !== process.env.prefix ? encodeURIComponent( patreons[channel.guild.id] + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator + '&useskin=oasis';
+					if ( wiki.isGamepedia() ) help_link = lang.get('verify.help_gamepedia') + '?c=' + ( patreons[channel.guild.id] && patreons[channel.guild.id] !== process.env.prefix ? encodeURIComponent( patreons[channel.guild.id] + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + toTitle(username) + '&discord=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator + '&useskin=fandomdesktop';
+					else if ( wiki.isFandom() ) help_link = lang.get('verify.help_fandom') + '/' + toTitle(username) + '?c=' + ( patreons[channel.guild.id] && patreons[channel.guild.id] !== process.env.prefix ? encodeURIComponent( patreons[channel.guild.id] + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator + '&useskin=fandomdesktop';
 					if ( help_link.length ) embed.addField( lang.get('verify.notice'), lang.get('verify.help_guide', help_link, queryuser.gender) + '\n' + help_link );
 					if ( help_link.length ) embed.addField( lang.get('verify.notice'), lang.get('verify.help_guide', help_link, queryuser.gender) + '\n' + help_link );
 					result.content = lang.get('verify.user_failed_reply', escapeFormatting(username), queryuser.gender);
 					result.content = lang.get('verify.user_failed_reply', escapeFormatting(username), queryuser.gender);
 					return;
 					return;

+ 1 - 0
i18n/en.json

@@ -578,6 +578,7 @@
         "wiki": "Wiki:"
         "wiki": "Wiki:"
     },
     },
     "search": {
     "search": {
+        "cached": "Cache last updated:",
         "category": {
         "category": {
             "content": "Content of this category:",
             "content": "Content of this category:",
             "empty": "*This category is empty*",
             "empty": "*This category is empty*",

+ 10 - 2
main.js

@@ -26,7 +26,7 @@ var diedShards = 0;
 manager.on( 'shardCreate', shard => {
 manager.on( 'shardCreate', shard => {
 	console.log( `- Shard[${shard.id}]: Launched` );
 	console.log( `- Shard[${shard.id}]: Launched` );
 	
 	
-	shard.on( 'spawn', message => {
+	shard.on( 'spawn', () => {
 		console.log( `- Shard[${shard.id}]: Spawned` );
 		console.log( `- Shard[${shard.id}]: Spawned` );
 		shard.send( {
 		shard.send( {
 			shard: {
 			shard: {
@@ -77,11 +77,19 @@ manager.spawn().then( shards => {
 	}
 	}
 }, error => {
 }, error => {
 	console.error( '- Error while spawning the shards: ' + error );
 	console.error( '- Error while spawning the shards: ' + error );
+	manager.shards.filter( shard => shard.process && !shard.process.killed ).forEach( shard => shard.kill() );
 	if ( isDebug ) {
 	if ( isDebug ) {
+		manager.respawn = false;
 		if ( typeof server !== 'undefined' && !server.killed ) server.kill();
 		if ( typeof server !== 'undefined' && !server.killed ) server.kill();
 		process.exit(1);
 		process.exit(1);
 	}
 	}
-	else manager.respawnAll();
+	else manager.spawn(manager.totalShards, 5500, -1).catch( error2 => {
+		console.error( '- Error while spawning the shards: ' + error2 );
+		manager.respawn = false;
+		manager.shards.filter( shard => shard.process && !shard.process.killed ).forEach( shard => shard.kill() );
+		if ( typeof server !== 'undefined' && !server.killed ) server.kill();
+		process.exit(1);
+	} );
 } );
 } );
 
 
 var server;
 var server;

+ 1 - 0
util/default.json

@@ -69,6 +69,7 @@
 			"autopatrol",
 			"autopatrol",
 			"rollback",
 			"rollback",
 			"autoconfirmed",
 			"autoconfirmed",
+			"emailconfirmed",
 			"user"
 			"user"
 		],
 		],
 		"global": [
 		"global": [