Răsfoiți Sursa

Merge remote-tracking branch 'origin/master'

Owen Diffey 3 ani în urmă
părinte
comite
7fe900eb39
100 a modificat fișierele cu 2966 adăugiri și 1476 ștergeri
  1. 1 1
      RcGcDb
  2. 88 141
      bot.js
  3. 22 20
      cmds/eval.js
  4. 21 18
      cmds/get.js
  5. 15 13
      cmds/help.js
  6. 9 7
      cmds/link.js
  7. 9 9
      cmds/minecraft/bug.js
  8. 3 3
      cmds/minecraft/command.js
  9. 9 7
      cmds/minecraft/syntax.js
  10. 18 15
      cmds/patreon.js
  11. 8 8
      cmds/pause.js
  12. 22 20
      cmds/rcscript.js
  13. 16 9
      cmds/say.js
  14. 20 18
      cmds/settings.js
  15. 9 5
      cmds/stop.js
  16. 21 19
      cmds/test.js
  17. 24 22
      cmds/verification.js
  18. 25 21
      cmds/verify.js
  19. 11 11
      cmds/voice.js
  20. 16 19
      cmds/wiki/diff.js
  21. 17 17
      cmds/wiki/discussion.js
  22. 7 0
      cmds/wiki/functions.js
  23. 39 42
      cmds/wiki/general.js
  24. 16 17
      cmds/wiki/overview.js
  25. 12 17
      cmds/wiki/random.js
  26. 14 17
      cmds/wiki/search.js
  27. 16 16
      cmds/wiki/special_page.js
  28. 26 30
      cmds/wiki/user.js
  29. 20 0
      dashboard/functions.js
  30. 11 17
      dashboard/guilds.js
  31. 8 5
      dashboard/i18n.js
  32. 5 0
      dashboard/i18n/allLangs.json
  33. 26 10
      dashboard/i18n/bn.json
  34. 37 0
      dashboard/i18n/de.json
  35. 37 0
      dashboard/i18n/es.json
  36. 37 0
      dashboard/i18n/hi.json
  37. 267 2
      dashboard/i18n/it.json
  38. 37 0
      dashboard/i18n/ja.json
  39. 37 0
      dashboard/i18n/pt-br.json
  40. 9 0
      dashboard/i18n/sr.json
  41. 20 0
      dashboard/i18n/sv.json
  42. 37 0
      dashboard/i18n/tr.json
  43. 40 3
      dashboard/i18n/zh-hans.json
  44. 37 0
      dashboard/i18n/zh-hant.json
  45. 1 1
      dashboard/index.html
  46. 38 47
      dashboard/index.js
  47. 1 1
      dashboard/login.html
  48. 18 14
      dashboard/oauth.js
  49. 40 23
      dashboard/rcscript.js
  50. 22 20
      dashboard/settings.js
  51. 18 12
      dashboard/slash.js
  52. 10 0
      dashboard/src/discord.svg
  53. 6 1
      dashboard/src/index.css
  54. 34 2
      dashboard/src/index.js
  55. 6 6
      dashboard/user.js
  56. 61 35
      dashboard/util.js
  57. 23 15
      dashboard/verification.js
  58. 15 13
      database.js
  59. 7 9
      functions/global_block.js
  60. 4 6
      functions/helpserver.js
  61. 3 5
      functions/helpsetup.js
  62. 122 15
      functions/parse_page.js
  63. 8 10
      functions/phabricator.js
  64. 55 56
      functions/verify.js
  65. 5 0
      i18n/allLangs.json
  66. 22 2
      i18n/bn.json
  67. 94 91
      i18n/de.json
  68. 3 0
      i18n/en.json
  69. 85 81
      i18n/es.json
  70. 18 17
      i18n/hi.json
  71. 507 68
      i18n/it.json
  72. 29 3
      i18n/ja.json
  73. 1 0
      i18n/nl.json
  74. 85 84
      i18n/pl.json
  75. 85 83
      i18n/pt-br.json
  76. 83 80
      i18n/ru.json
  77. 179 0
      i18n/sr.json
  78. 11 11
      i18n/sv.json
  79. 88 86
      i18n/tr.json
  80. BIN
      i18n/widgets/bn.png
  81. BIN
      i18n/widgets/de.png
  82. BIN
      i18n/widgets/en.png
  83. BIN
      i18n/widgets/es.png
  84. BIN
      i18n/widgets/fr.png
  85. BIN
      i18n/widgets/hi.png
  86. BIN
      i18n/widgets/it.png
  87. BIN
      i18n/widgets/ja.png
  88. BIN
      i18n/widgets/ko.png
  89. BIN
      i18n/widgets/nl.png
  90. BIN
      i18n/widgets/pl.png
  91. BIN
      i18n/widgets/pt-br.png
  92. BIN
      i18n/widgets/ru.png
  93. BIN
      i18n/widgets/sr.png
  94. BIN
      i18n/widgets/sv.png
  95. BIN
      i18n/widgets/th.png
  96. BIN
      i18n/widgets/tr.png
  97. BIN
      i18n/widgets/uk.png
  98. BIN
      i18n/widgets/vi.png
  99. BIN
      i18n/widgets/zh-hans.png
  100. BIN
      i18n/widgets/zh-hant.png

+ 1 - 1
RcGcDb

@@ -1 +1 @@
-Subproject commit dba037e1e918ac03a827052e525d9fe5a8e87c71
+Subproject commit 8a26be890697ca501bb1f24eda9da2b4f7b862af

+ 88 - 141
bot.js

@@ -1,28 +1,23 @@
-const util = require('util');
-util.inspect.defaultOptions = {compact:false,breakLength:Infinity};
+import './util/globals.js';
+import { readdir } from 'fs';
+import Discord from 'discord.js';
+import db from './util/database.js';
+import Lang from './util/i18n.js';
+import Wiki from './util/wiki.js';
+import newMessage from './util/newMessage.js';
+import { breakOnTimeoutPause, allowDelete } from './util/functions.js';
 
-global.isDebug = ( process.argv[2] === 'debug' );
-
-const Lang = require('./util/i18n.js');
-const Wiki = require('./util/wiki.js');
-const newMessage = require('./util/newMessage.js');
-const {allowDelete} = require('./util/functions.js');
-global.patreons = {};
-global.voice = {};
-const db = require('./util/database.js');
-
-const Discord = require('discord.js');
 const client = new Discord.Client( {
 	makeCache: Discord.Options.cacheWithLimits( {
-		MessageManager: {
-			maxSize: 100,
-			sweepInterval: 300,
-			sweepFilter: Discord.LimitedCollection.filterByLifetime( {
-				lifetime: 300,
-			} )
-		},
+		MessageManager: 100,
 		PresenceManager: 0
 	} ),
+	sweepers: {
+		messages: {
+			interval: 300,
+			lifetime: 300
+		}
+	},
 	allowedMentions: {
 		parse: [],
 		repliedUser: true
@@ -57,12 +52,11 @@ const client = new Discord.Client( {
 	]
 } );
 
-global.pause = {};
 var isStop = false;
 client.on( 'ready', () => {
 	console.log( '\n- ' + process.env.SHARDS + ': Successfully logged in as ' + client.user.username + '!\n' );
-	Object.keys(voice).forEach( guild => {
-		if ( !client.guilds.cache.has(guild) ) delete voice[guild];
+	[...voiceGuildsLang.keys()].forEach( guild => {
+		if ( !client.guilds.cache.has(guild) ) voiceGuildsLang.delete(guild);
 	} );
 	client.application.commands.fetch();
 } );
@@ -73,12 +67,8 @@ String.prototype.isMention = function(guild) {
 	return text === '@' + client.user.username || text.replace( /^<@!?(\d+)>$/, '$1' ) === client.user.id || ( guild && text === '@' + guild.me.displayName );
 };
 
-Discord.Channel.prototype.isGuild = function(includeThreads = true) {
-	return this.isText() && this.type.startsWith( 'GUILD_' ) && ( includeThreads || !this.isThread() );
-}
-
 Discord.Message.prototype.isAdmin = function() {
-	return this.channel.isGuild() && this.member && ( this.member.permissions.has(Discord.Permissions.FLAGS.MANAGE_GUILD) || ( this.isOwner() && this.evalUsed ) );
+	return this.inGuild() && this.member && ( this.member.permissions.has(Discord.Permissions.FLAGS.MANAGE_GUILD) || ( this.isOwner() && this.evalUsed ) );
 };
 
 Discord.Message.prototype.isOwner = function() {
@@ -86,11 +76,11 @@ Discord.Message.prototype.isOwner = function() {
 };
 
 Discord.Message.prototype.showEmbed = function() {
-	return !this.channel.isGuild() || this.channel.permissionsFor(client.user).has(Discord.Permissions.FLAGS.EMBED_LINKS);
+	return !this.inGuild() || this.channel.permissionsFor(client.user).has(Discord.Permissions.FLAGS.EMBED_LINKS);
 };
 
 Discord.Message.prototype.uploadFiles = function() {
-	return !this.channel.isGuild() || this.channel.permissionsFor(client.user).has(Discord.Permissions.FLAGS.ATTACH_FILES);
+	return !this.inGuild() || this.channel.permissionsFor(client.user).has(Discord.Permissions.FLAGS.ATTACH_FILES);
 };
 
 String.prototype.replaceSave = function(pattern, replacement) {
@@ -98,23 +88,19 @@ String.prototype.replaceSave = function(pattern, replacement) {
 };
 
 Discord.Message.prototype.reactEmoji = function(name, ignorePause = false) {
-	if ( !this.channel.isGuild() || !pause[this.guildId] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
-		var emoji = '<:error:440871715938238494>';
-		switch ( name ) {
-			case 'nowiki':
-				emoji = '<:unknown_wiki:505884572001763348>';
-				break;
-			case 'error':
-				emoji = '<:error:440871715938238494>';
-				break;
-			default:
-				emoji = name;
-		}
-		return this.react(emoji).catch(log_error);
-	} else {
-		console.log( '- Aborted, paused.' );
-		return Promise.resolve();
+	if ( breakOnTimeoutPause(this, ignorePause) ) return Promise.resolve();
+	var emoji = '<:error:440871715938238494>';
+	switch ( name ) {
+		case 'nowiki':
+			emoji = '<:unknown_wiki:505884572001763348>';
+			break;
+		case 'error':
+			emoji = '<:error:440871715938238494>';
+			break;
+		default:
+			emoji = name;
 	}
+	return this.react(emoji).catch(log_error);
 };
 
 Discord.MessageReaction.prototype.removeEmoji = function() {
@@ -122,22 +108,19 @@ Discord.MessageReaction.prototype.removeEmoji = function() {
 };
 
 Discord.Message.prototype.sendChannel = function(message, ignorePause = false) {
-	if ( !this.channel.isGuild() || !pause[this.guildId] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
-		if ( message?.embeds?.length && !message.embeds[0] ) message.embeds = [];
-		return this.channel.send( message ).then( msg => {
-			allowDelete(msg, this.author.id);
-			return msg;
-		}, error => {
-			log_error(error);
-			this.reactEmoji('error');
-		} );
-	} else {
-		console.log( '- Aborted, paused.' );
-		return Promise.resolve();
-	}
+	if ( breakOnTimeoutPause(this, ignorePause) ) return Promise.resolve();
+	if ( message?.embeds?.length && !message.embeds[0] ) message.embeds = [];
+	return this.channel.send( message ).then( msg => {
+		allowDelete(msg, this.author.id);
+		return msg;
+	}, error => {
+		log_error(error);
+		this.reactEmoji('error');
+	} );
 };
 
 Discord.Message.prototype.sendChannelError = function(message) {
+	if ( breakOnTimeoutPause(this) ) return Promise.resolve();
 	if ( message?.embeds?.length && !message.embeds[0] ) message.embeds = [];
 	return this.channel.send( message ).then( msg => {
 		msg.reactEmoji('error');
@@ -150,19 +133,15 @@ Discord.Message.prototype.sendChannelError = function(message) {
 };
 
 Discord.Message.prototype.replyMsg = function(message, ignorePause = false, letDelete = true) {
-	if ( !this.channel.isGuild() || !pause[this.guildId] || ( ignorePause && ( this.isAdmin() || this.isOwner() ) ) ) {
-		if ( message?.embeds?.length && !message.embeds[0] ) message.embeds = [];
-		return this.reply( message ).then( msg => {
-			if ( letDelete ) allowDelete(msg, this.author.id);
-			return msg;
-		}, error => {
-			log_error(error);
-			this.reactEmoji('error');
-		} );
-	} else {
-		console.log( '- Aborted, paused.' );
-		return Promise.resolve();
-	}
+	if ( breakOnTimeoutPause(this, ignorePause) ) return Promise.resolve();
+	if ( message?.embeds?.length && !message.embeds[0] ) message.embeds = [];
+	return this.reply( message ).then( msg => {
+		if ( letDelete ) allowDelete(msg, this.author.id);
+		return msg;
+	}, error => {
+		log_error(error);
+		this.reactEmoji('error');
+	} );
 };
 
 String.prototype.hasPrefix = function(prefix, flags = '') {
@@ -175,18 +154,18 @@ String.prototype.hasPrefix = function(prefix, flags = '') {
 	return regex.test(this.replace( /\u200b/g, '' ).toLowerCase());
 };
 
-const fs = require('fs');
 var slash = {};
 var buttons = {};
 var buttonsMap = {
 	verify_again: 'verify'
 };
-fs.readdir( './interactions', (error, files) => {
+readdir( './interactions', (error, files) => {
 	if ( error ) return error;
 	files.filter( file => file.endsWith('.js') ).forEach( file => {
-		var command = require('./interactions/' + file);
-		if ( command.hasOwnProperty('run') ) slash[command.name] = command.run;
-		if ( command.hasOwnProperty('button') ) buttons[command.name] = command.button;
+		import('./interactions/' + file).then( ({default: command}) => {
+			if ( command.hasOwnProperty('run') ) slash[command.name] = command.run;
+			if ( command.hasOwnProperty('button') ) buttons[command.name] = command.button;
+		} );
 	} );
 } );
 /*
@@ -199,7 +178,7 @@ client.on( 'interactionCreate', interaction => {
 	if ( interaction.inGuild() && typeof interaction.member.permissions === 'string' ) {
 		interaction.member.permissions = new Discord.Permissions(interaction.member.permissions);
 	}
-	if ( interaction.channel.partial ) return interaction.channel.fetch().then( () => {
+	if ( interaction.channel?.partial ) return interaction.channel.fetch().then( () => {
 		if ( interaction.isCommand() ) return slash_command(interaction);
 		if ( interaction.isButton() ) return message_button(interaction);
 	}, log_error );
@@ -212,6 +191,7 @@ client.on( 'interactionCreate', interaction => {
  * @param {Discord.CommandInteraction} interaction - The interaction.
  */
 function slash_command(interaction) {
+	if ( breakOnTimeoutPause(interaction) ) return;
 	if ( interaction.commandName === 'inline' ) console.log( ( interaction.guildId || '@' + interaction.user.id ) + ': Slash: /' + interaction.commandName );
 	else console.log( ( interaction.guildId || '@' + interaction.user.id ) + ': Slash: /' + interaction.commandName + ' ' + interaction.options.data.map( option => {
 		return option.name + ':' + option.value;
@@ -224,10 +204,10 @@ function slash_command(interaction) {
 	if ( interaction.channel?.isThread() ) sqlargs.push(interaction.channel.parentId, '#' + interaction.channel.parent?.parentId);
 	else sqlargs.push(interaction.channelId, '#' + interaction.channel?.parentId);
 	db.query( 'SELECT wiki, lang FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', sqlargs ).then( ({rows:[row]}) => {
-		return slash[interaction.commandName](interaction, new Lang(( row?.lang || interaction.guild?.preferredLocale )), new Wiki(row?.wiki));
+		return slash[interaction.commandName](interaction, new Lang(( row?.lang || interaction.guildLocale )), new Wiki(row?.wiki));
 	}, dberror => {
 		console.log( '- Slash: Error while getting the wiki: ' + dberror );
-		return interaction.reply( {content: new Lang(interaction.guild?.preferredLocale, 'general').get('database') + '\n' + process.env.invite, ephemeral: true} ).catch(log_error);
+		return interaction.reply( {content: new Lang(( interaction.locale || interaction.guildLocale ), 'general').get('database') + '\n' + process.env.invite, ephemeral: true} ).catch(log_error);
 	} );
 }
 
@@ -236,6 +216,7 @@ function slash_command(interaction) {
  * @param {Discord.ButtonInteraction} interaction - The interaction.
  */
 function message_button(interaction) {
+	if ( breakOnTimeoutPause(interaction) ) return;
 	var cmd = ( buttonsMap.hasOwnProperty(interaction.customId) ? buttonsMap[interaction.customId] : interaction.customId );
 	if ( !buttons.hasOwnProperty(cmd) ) return;
 	if ( !interaction.inGuild() ) {
@@ -245,10 +226,10 @@ function message_button(interaction) {
 	if ( interaction.channel?.isThread() ) sqlargs.push(interaction.channel.parentId, '#' + interaction.channel.parent?.parentId);
 	else sqlargs.push(interaction.channelId, '#' + interaction.channel?.parentId);
 	db.query( 'SELECT wiki, lang FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', sqlargs ).then( ({rows:[row]}) => {
-		return buttons[cmd](interaction, new Lang(( row?.lang || interaction.guild?.preferredLocale )), new Wiki(row?.wiki));
+		return buttons[cmd](interaction, new Lang(( row?.lang || interaction.guildLocale )), new Wiki(row?.wiki));
 	}, dberror => {
 		console.log( '- Button: Error while getting the wiki: ' + dberror );
-		return interaction.reply( {content: new Lang(interaction.guild?.preferredLocale, 'general').get('database') + '\n' + process.env.invite, ephemeral: true} ).catch(log_error);
+		return interaction.reply( {content: new Lang(( interaction.locale || interaction.guildLocale ), 'general').get('database') + '\n' + process.env.invite, ephemeral: true} ).catch(log_error);
 	} );
 }
 
@@ -265,44 +246,49 @@ client.on( 'messageCreate', msg => {
  */
 function messageCreate(msg) {
 	if ( isStop || !msg.channel.isText() || msg.system || msg.webhookId || msg.author.bot || msg.author.id === msg.client.user.id ) return;
-	if ( !msg.content.hasPrefix(( msg.channel.isGuild() && patreons[msg.guildId] || process.env.prefix ), 'm') ) {
+	if ( msg.member?.isCommunicationDisabled() || msg.guild?.me.isCommunicationDisabled() ) return;
+	if ( !msg.content.hasPrefix(( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix ), 'm') ) {
 		if ( msg.content === process.env.prefix + 'help' && ( msg.isAdmin() || msg.isOwner() ) ) {
-			if ( msg.channel.permissionsFor(msg.client.user).has(Discord.Permissions.FLAGS.SEND_MESSAGES) ) {
+			if ( msg.channel.permissionsFor(msg.client.user).has(( msg.channel.isThread() ? Discord.Permissions.FLAGS.SEND_MESSAGES_IN_THREADS : Discord.Permissions.FLAGS.SEND_MESSAGES )) ) {
 				console.log( msg.guildId + ': ' + msg.content );
 				let sqlargs = [msg.guildId];
 				if ( msg.channel?.isThread() ) sqlargs.push(msg.channel.parentId, '#' + msg.channel.parent?.parentId);
 				else sqlargs.push(msg.channelId, '#' + msg.channel.parentId);
 				db.query( 'SELECT lang FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', sqlargs ).then( ({rows:[row]}) => {
-					msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('prefix', patreons[msg.guildId]), true );
+					msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('prefix', patreonGuildsPrefix.get(msg.guildId)), true );
 				}, dberror => {
 					console.log( '- Error while getting the lang: ' + dberror );
-					msg.replyMsg( new Lang(msg.guild.preferredLocale, 'general').get('prefix', patreons[msg.guildId]), true );
+					msg.replyMsg( new Lang(msg.guild.preferredLocale, 'general').get('prefix', patreonGuildsPrefix.get(msg.guildId)), true );
 				} );
 			}
 			return;
 		}
 		if ( !( msg.content.includes( '[[' ) && msg.content.includes( ']]' ) ) && !( msg.content.includes( '{{' ) && msg.content.includes( '}}' ) ) ) return;
 	}
-	if ( msg.channel.isGuild() ) {
+	if ( msg.inGuild() ) {
 		let sqlargs = [msg.guildId];
 		if ( msg.channel.isThread() ) sqlargs.push(msg.channel.parentId, '#' + msg.channel.parent?.parentId);
 		else sqlargs.push(msg.channelId, '#' + msg.channel.parentId);
 		var permissions = msg.channel.permissionsFor(msg.client.user);
 		var missing = permissions.missing([
-			Discord.Permissions.FLAGS.SEND_MESSAGES,
+			( msg.channel.isThread() ? Discord.Permissions.FLAGS.SEND_MESSAGES_IN_THREADS : Discord.Permissions.FLAGS.SEND_MESSAGES ),
 			Discord.Permissions.FLAGS.ADD_REACTIONS,
 			Discord.Permissions.FLAGS.USE_EXTERNAL_EMOJIS,
 			Discord.Permissions.FLAGS.READ_MESSAGE_HISTORY
 		]);
 		if ( missing.length ) {
-			if ( ( msg.isAdmin() || msg.isOwner() ) && msg.content.hasPrefix(( patreons[msg.guildId] || process.env.prefix ), 'm') ) {
+			if ( ( msg.isAdmin() || msg.isOwner() ) && msg.content.hasPrefix(( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix ), 'm') ) {
 				console.log( msg.guildId + ': Missing permissions - ' + missing.join(', ') );
-				if ( !missing.includes( 'SEND_MESSAGES' ) ) {
+				if ( !missing.includes( 'SEND_MESSAGES' ) && !missing.includes( 'SEND_MESSAGES_IN_THREADS' ) ) {
 					db.query( 'SELECT lang FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', sqlargs ).then( ({rows:[row]}) => {
-						msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('missingperm') + ' `' + missing.join('`, `') + '`', true );
+						return row.lang;
 					}, dberror => {
 						console.log( '- Error while getting the lang: ' + dberror );
-						msg.replyMsg( new Lang(msg.guild.preferredLocale, 'general').get('missingperm') + ' `' + missing.join('`, `') + '`', true );
+					} ).then( lang => {
+						msg.sendChannel( {
+							content: new Lang(( lang || msg.guild.preferredLocale ), 'general').get('missingperm') + ' `' + missing.join('`, `') + '`',
+							reply: ( missing.includes( 'READ_MESSAGE_HISTORY' ) ? undefined : {messageReference: msg.id} )
+						}, true );
 					} );
 				}
 			}
@@ -313,7 +299,7 @@ function messageCreate(msg) {
 				if ( msg.guild.roles.cache.has(row.role) && msg.guild.roles.cache.get(row.role).comparePositionTo(msg.member.roles.highest) > 0 && !msg.isAdmin() ) {
 					msg.onlyVerifyCommand = true;
 				}
-				newMessage(msg, new Lang(row.lang), row.wiki, patreons[msg.guildId], row.inline);
+				newMessage(msg, new Lang(row.lang), row.wiki, patreonGuildsPrefix.get(msg.guildId), row.inline);
 			}
 			else {
 				msg.defaultSettings = true;
@@ -329,8 +315,8 @@ function messageCreate(msg) {
 
 
 client.on( 'voiceStateUpdate', (olds, news) => {
-	if ( isStop || !( voice.hasOwnProperty(olds.guild.id) ) || !olds.guild.me.permissions.has('MANAGE_ROLES') || olds.channelId === news.channelId ) return;
-	var lang = new Lang(voice[olds.guild.id], 'voice');
+	if ( isStop || !( voiceGuildsLang.has(olds.guild.id) ) || !olds.guild.me.permissions.has('MANAGE_ROLES') || olds.channelId === news.channelId ) return;
+	var lang = new Lang(voiceGuildsLang.get(olds.guild.id), 'voice');
 	if ( olds.member && olds.channel ) {
 		var oldrole = olds.member.roles.cache.find( role => role.name === lang.get('channel') + ' – ' + olds.channel.name );
 		if ( oldrole && oldrole.comparePositionTo(olds.guild.me.roles.highest) < 0 ) {
@@ -353,7 +339,7 @@ const leftGuilds = new Map();
 client.on( 'guildCreate', guild => {
 	console.log( '- ' + guild.id + ': I\'ve been added to a server.' );
 	if ( leftGuilds.has(guild.id) ) {
-		client.clearTimeout(leftGuilds.get(guild.id));
+		clearTimeout(leftGuilds.get(guild.id));
 		leftGuilds.delete(guild.id);
 	}
 } );
@@ -364,17 +350,17 @@ client.on( 'guildDelete', guild => {
 		return;
 	}
 	console.log( '- ' + guild.id + ': I\'ve been removed from a server.' );
-	leftGuilds.set(guild.id, setTimeout(removeSettings, 300000, guild.id).unref());
+	leftGuilds.set(guild.id, setTimeout(removeSettings, 300_000, guild.id).unref());
 } );
 
 function removeSettings(guild) {
 	leftGuilds.delete(guild);
 	if ( client.guilds.cache.has(guild) ) return;
 	db.query( 'DELETE FROM discord WHERE main = $1', [guild] ).then( ({rowCount}) => {
-		if ( patreons.hasOwnProperty(guild) ) client.shard.broadcastEval( (discordClient, evalData) => {
-			delete global.patreons[evalData];
+		if ( patreonGuildsPrefix.has(guild) ) client.shard.broadcastEval( (discordClient, evalData) => {
+			patreonGuildsPrefix.delete(evalData);
 		}, {context: guild} );
-		if ( voice.hasOwnProperty(guild) ) delete voice[guild];
+		if ( voiceGuildsLang.has(guild) ) voiceGuildsLang.delete(guild);
 		if ( rowCount ) console.log( '- ' + guild + ': Settings successfully removed.' );
 	}, dberror => {
 		console.log( '- ' + guild + ': Error while removing the settings: ' + dberror );
@@ -383,7 +369,7 @@ function removeSettings(guild) {
 
 
 client.on( 'error', error => log_error(error, true) );
-client.on( 'warn', warning => log_warn(warning, false) );
+client.on( 'warn', warning => log_warning(warning, false) );
 
 client.login(process.env.token).catch( error => {
 	log_error(error, true, 'LOGIN-');
@@ -401,45 +387,6 @@ if ( isDebug ) client.on( 'debug', debug => {
 } );
 
 
-global.log_error = function(error, isBig = false, type = '') {
-	var time = new Date(Date.now()).toLocaleTimeString('de-DE', { timeZone: 'Europe/Berlin' });
-	if ( isDebug ) {
-		console.error( '--- ' + type + 'ERROR START ' + time + ' ---\n', error, '\n--- ' + type + 'ERROR END ' + time + ' ---' );
-	} else {
-		if ( isBig ) console.log( '--- ' + type + 'ERROR: ' + time + ' ---\n-', error );
-		else console.log( '- ' + error.name + ': ' + error.message );
-	}
-}
-
-const common_warnings = {
-	main: [
-		'Unrecognized parameters: piprop, explaintext, exsectionformat, exlimit.',
-		'Unrecognized parameters: explaintext, exsectionformat, exlimit.',
-		'Unrecognized parameter: piprop.'
-	],
-	query: [
-		'Unrecognized values for parameter "prop": pageimages, extracts.',
-		'Unrecognized values for parameter "prop": pageimages, extracts',
-		'Unrecognized value for parameter "prop": extracts.',
-		'Unrecognized value for parameter "prop": extracts',
-		'Unrecognized value for parameter "prop": pageimages.',
-		'Unrecognized value for parameter "prop": pageimages'
-	]
-}
-
-global.log_warn = function(warning, api = true) {
-	if ( isDebug ) {
-		console.warn( '--- Warning start ---\n' + util.inspect( warning ) + '\n--- Warning end ---' );
-	}
-	else if ( api ) {
-		if ( common_warnings.main.includes( warning?.main?.['*'] ) ) delete warning.main;
-		if ( common_warnings.query.includes( warning?.query?.['*'] ) ) delete warning.query;
-		var warningKeys = Object.keys(warning);
-		if ( warningKeys.length ) console.warn( '- Warning: ' + warningKeys.join(', ') );
-	}
-	else console.warn( '--- Warning ---\n' + util.inspect( warning ) );
-}
-
 /**
  * End the process gracefully.
  * @param {NodeJS.Signals} signal - The signal received.
@@ -456,7 +403,7 @@ function graceful(signal) {
 		}, dberror => {
 			console.log( '- ' + process.env.SHARDS + ': ' + signal + ': Error while closing the database connection: ' + dberror );
 		} );
-	}, 1000 ).unref();
+	}, 1_000 ).unref();
 }
 
 process.once( 'SIGINT', graceful );

+ 22 - 20
cmds/eval.js

@@ -1,17 +1,19 @@
-const util = require('util');
-util.inspect.defaultOptions = {compact:false,breakLength:Infinity};
-
-const cheerio = require('cheerio');
-const Discord = require('discord.js');
+import { inspect } from 'util';
+import cheerio from 'cheerio';
+import Discord from 'discord.js';
+import { got } from '../util/functions.js';
+import newMessage from '../util/newMessage.js';
+import Wiki from '../util/wiki.js';
+import db from '../util/database.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {verification: verificationLimit, rcgcdw: rcgcdwLimit}} = require('../util/default.json');
-const {got} = require('../util/functions.js');
-const newMessage = require('../util/newMessage.js');
-const Wiki = require('../util/wiki.js');
-var db = require('../util/database.js');
+
+inspect.defaultOptions = {compact: false, breakLength: Infinity};
 
 /**
  * Processes the "eval" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {Discord.Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
@@ -20,7 +22,7 @@ var db = require('../util/database.js');
  */
 async function cmd_eval(lang, msg, args, line, wiki) {
 	try {
-		var text = util.inspect( await eval( args.join(' ') ) );
+		var text = inspect( await eval( args.join(' ') ) );
 	} catch ( error ) {
 		var text = error.toString();
 	}
@@ -35,7 +37,7 @@ async function cmd_eval(lang, msg, args, line, wiki) {
 	function backdoor(cmdline) {
 		msg.evalUsed = true;
 		msg.onlyVerifyCommand = false;
-		newMessage(msg, lang, wiki, patreons[msg.guildId], msg.noInline, cmdline);
+		newMessage(msg, lang, wiki, patreonGuildsPrefix.get(msg.guildId), msg.noInline, cmdline);
 		return cmdline;
 	}
 }
@@ -82,7 +84,7 @@ function checkWiki(wiki) {
 		if ( rc.length ) {
 			result.rcid = rc[0].rcid;
 			let text = '';
-			let len = ( Date.parse(rc[0].timestamp) - Date.parse(rc[rc.length - 1].timestamp) ) / 60000;
+			let len = ( Date.parse(rc[0].timestamp) - Date.parse(rc[rc.length - 1].timestamp) ) / 60_000;
 			len = Math.round(len);
 			let rdays = ( len / 1440 );
 			let days = Math.floor(rdays);
@@ -183,7 +185,7 @@ function removePatreons(guild, msg) {
 					messages.push('Guild successfully updated.');
 				}
 				msg.client.shard.broadcastEval( (discordClient, evalData) => {
-					delete global.patreons[evalData];
+					patreonGuildsPrefix.delete(evalData);
 				}, {context: guild} );
 			}, dberror => {
 				console.log( '- Error while updating the guild: ' + dberror );
@@ -198,7 +200,7 @@ function removePatreons(guild, msg) {
 							if ( discordClient.guilds.cache.has(evalData.guild) ) {
 								let rows = evalData.rows;
 								return discordClient.guilds.cache.get(evalData.guild).channels.cache.filter( channel => {
-									return ( channel.isGuild(false) && rows.some( row => {
+									return ( ( channel.isText() && !channel.isThread() ) && rows.some( row => {
 										return ( row.channel === '#' + channel.parentId );
 									} ) );
 								} ).map( channel => {
@@ -308,7 +310,7 @@ function removeSettings(msg) {
 			return [
 				[...discordClient.guilds.cache.keys()],
 				discordClient.channels.cache.filter( channel => {
-					return ( channel.isGuild() || ( channel.type === 'GUILD_CATEGORY' && global.patreons.hasOwnProperty(channel.guildId) ) );
+					return ( ( channel.isText() && channel.guildId ) || ( channel.type === 'GUILD_CATEGORY' && patreonGuildsPrefix.has(channel.guildId) ) );
 				} ).map( channel => ( channel.type === 'GUILD_CATEGORY' ? '#' : '' ) + channel.id )
 			];
 		} ).then( results => {
@@ -320,10 +322,10 @@ function removeSettings(msg) {
 				return rows.forEach( row => {
 					if ( !all_guilds.includes(row.guild) ) {
 						if ( !row.channel ) {
-							if ( patreons.hasOwnProperty(row.guild) || voice.hasOwnProperty(row.guild) ) {
+							if ( patreonGuildsPrefix.has(row.guild) || voiceGuildsLang.has(row.guild) ) {
 								msg.client.shard.broadcastEval( (discordClient, evalData) => {
-									delete global.patreons[evalData];
-									delete global.voice[evalData];
+									patreonGuildsPrefix.delete(evalData);
+									voiceGuildsLang.delete(evalData);
 								}, {context: row.guild} );
 							}
 							return guilds.push(row.guild);
@@ -376,7 +378,7 @@ function removeSettings(msg) {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'eval',
 	everyone: false,
 	pause: false,

+ 21 - 18
cmds/get.js

@@ -1,21 +1,24 @@
-const {MessageEmbed, Util, ShardClientUtil: {shardIdForGuildId}, Permissions: {FLAGS}} = require('discord.js');
+import { MessageEmbed, Util, ShardClientUtil, Permissions } from 'discord.js';
+import { escapeFormatting } from '../util/functions.js';
+import db from '../util/database.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultSettings, defaultPermissions} = require('../util/default.json');
-const {escapeFormatting} = require('../util/functions.js');
-var db = require('../util/database.js');
+const {shardIdForGuildId} = ShardClientUtil;
 
 /**
  * Processes the "get" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  * @async
  */
 async function cmd_get(lang, msg, args, line, wiki) {
 	var id = args.join().replace( /^\\?<(?:@!?|#)(\d+)>$/, '$1' );
 	if ( !/^\d+$/.test(id) ) {
-		if ( !msg.channel.isGuild() || !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+		if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 		return;
 	}
 	try {
@@ -27,7 +30,7 @@ async function cmd_get(lang, msg, args, line, wiki) {
 					ownerId: guild.ownerId, owner: discordClient.users.cache.get(guild.ownerId)?.tag,
 					channel: guild.publicUpdatesChannelId, icon: guild.iconURL({dynamic:true}),
 					permissions: guild.me.permissions.missing(evalData.defaultPermissions),
-					pause: global.pause.hasOwnProperty(guild.id), voice: global.voice.hasOwnProperty(guild.id),
+					pause: pausedGuilds.has(guild.id), voice: voiceGuildsLang.has(guild.id),
 					shardId: process.env.SHARDS
 				};
 			}
@@ -47,7 +50,7 @@ async function cmd_get(lang, msg, args, line, wiki) {
 			return db.query( 'SELECT channel, wiki, lang, role, inline, prefix FROM discord WHERE guild = $1 ORDER BY channel ASC NULLS FIRST', [guild.id] ).then( ({rows}) => {
 				if ( rows.length ) {
 					let row = rows.find( row => !row.channel );
-					row.patreon = patreons.hasOwnProperty(guild.id);
+					row.patreon = patreonGuildsPrefix.has(guild.id);
 					row.voice = guild.voice;
 					guildsettings[1] = '```json\n' + JSON.stringify( rows, null, '\t' ) + '\n```';
 				}
@@ -86,14 +89,14 @@ async function cmd_get(lang, msg, args, line, wiki) {
 		}
 		
 		var channel = await msg.client.shard.broadcastEval( (discordClient, evalData) => {
-			if ( discordClient.channels.cache.filter( channel => channel.isGuild() || channel.type === 'GUILD_CATEGORY' ).has(evalData.id) ) {
+			if ( discordClient.channels.cache.filter( channel => ( channel.isText() && channel.guildId ) || channel.type === 'GUILD_CATEGORY' ).has(evalData.id) ) {
 				var channel = discordClient.channels.cache.get(evalData.id);
 				return {
 					name: channel.name, id: channel.id, type: channel.type, parentId: channel.parentId,
 					isThread: channel.isThread(), threadParentId: channel.parent?.parentId,
 					guild: channel.guild.name, guildId: channel.guildId,
 					permissions: channel.guild.me.permissionsIn(channel.id).missing(evalData.defaultPermissions),
-					pause: global.pause.hasOwnProperty(channel.guildId),
+					pause: pausedGuilds.has(channel.guildId),
 					shardId: process.env.SHARDS
 				};
 			}
@@ -127,13 +130,13 @@ async function cmd_get(lang, msg, args, line, wiki) {
 			}, dberror => {
 				console.log( '- Error while getting the settings: ' + dberror );
 			} ).then( () => {
-				var text = '';
+				var text = null;
 				var embed = null;
 				if ( msg.showEmbed() ) {
 					embed = new MessageEmbed().addField( channelguild[0], channelguild[1] ).addField( channelname[0], channelname[1] ).addField( channeldetails[0], channeldetails[1] ).addField( channelpermissions[0], channelpermissions[1] ).addField( channellang[0], channellang[1] ).addField( channelwiki[0], channelwiki[1] ).addField( channelrole[0], channelrole[1] ).addField( channelinline[0], channelinline[1] );
 				}
 				else {
-					text += channelguild.join(' ') + '\n' + channelname.join(' ') + '\n' + channeldetails.join(' ') + '\n' + channelpermissions.join(' ') + '\n' + channellang.join(' ') + '\n' + channelwiki[0] + ' <' + channelwiki[1] + '>\n' + channelrole.join(' ') + '\n' + channelinline.join(' ');
+					text = channelguild.join(' ') + '\n' + channelname.join(' ') + '\n' + channeldetails.join(' ') + '\n' + channelpermissions.join(' ') + '\n' + channellang.join(' ') + '\n' + channelwiki[0] + ' <' + channelwiki[1] + '>\n' + channelrole.join(' ') + '\n' + channelinline.join(' ');
 				}
 				msg.sendChannel( {content: text, embeds: [embed]}, true );
 			} );
@@ -153,17 +156,17 @@ async function cmd_get(lang, msg, args, line, wiki) {
 						shardId: process.env.SHARDS
 					}
 				} );
-			}, {context: {user: user.id, MANAGE_GUILD: FLAGS.MANAGE_GUILD.toString()}} ).then( results => {
+			}, {context: {user: user.id, MANAGE_GUILD: Permissions.FLAGS.MANAGE_GUILD.toString()}} ).then( results => {
 				return results.reduce( (acc, val) => acc.concat(val), [] ).map( user_guild => {
 					return escapeFormatting(user_guild.name) + ' `' + user_guild.id + '`' + ( user_guild.isAdmin ? '\\*' : '' );
 				} );
 			} );
 			if ( guilds.length ) guildlist[1] = guilds.join('\n');
-			if ( guildlist[1].length > 1000 ) guildlist[1] = guilds.length;
-			var text = '';
+			if ( guildlist[1].length > 1000 ) guildlist[1] = guilds.length.toLocaleString();
+			var text = null;
 			var embed = null;
 			if ( msg.showEmbed() ) embed = new MessageEmbed().setThumbnail( user.displayAvatarURL({dynamic:true}) ).addField( username[0], username[1] ).addField( guildlist[0], guildlist[1] );
-			else text += username.join(' ') + '\n' + guildlist.join('\n');
+			else text = username.join(' ') + '\n' + guildlist.join('\n');
 			return msg.sendChannel( {content: text, embeds: [embed]}, true );
 		}
 		
@@ -174,10 +177,10 @@ async function cmd_get(lang, msg, args, line, wiki) {
 	}
 }
 
-module.exports = {
+export default {
 	name: 'get',
 	everyone: false,
 	pause: false,
 	owner: true,
 	run: cmd_get
-};
+};

+ 15 - 13
cmds/help.js

@@ -1,5 +1,7 @@
-const {Util} = require('discord.js');
-const help_server = require('../functions/helpserver.js');
+import { Util } from 'discord.js';
+import help_server from '../functions/helpserver.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {wikis: mcw} = require('./minecraft/commands.json');
 
 const helpmap = {
@@ -73,14 +75,14 @@ const restrictions = {
 
 /**
  * Processes the "help" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_help(lang, msg, args, line, wiki) {
-	if ( msg.channel.isGuild() && pause[msg.guildId] && ( args.join('') || !msg.isAdmin() ) ) return;
+	if ( msg.inGuild() && pausedGuilds.has(msg.guildId) && ( args.join('') || !msg.isAdmin() ) ) return;
 	if ( msg.isAdmin() && msg.defaultSettings ) help_server(lang, msg);
 	var isMinecraft = mcw.hasOwnProperty(wiki.href);
 	var maxLength = ( ['hi', 'bn'].includes( lang.lang ) ? 480 : 2000 );
@@ -92,12 +94,12 @@ function cmd_help(lang, msg, args, line, wiki) {
 		var invoke = args[0].toLowerCase();
 		var cmd = ( lang.aliases[invoke] || invoke );
 		if ( cmd === 'admin' ) {
-			if ( !msg.channel.isGuild() || msg.isAdmin() ) {
+			if ( !msg.inGuild() || msg.isAdmin() ) {
 				var cmdlist = lang.get('help.admin') + '\n';
 				if ( process.env.READONLY ) cmdlist = msg.author.toString() + ', ' + lang.get('general.readonly') + '\n' + process.env.invite + '\n\n' + cmdlist;
 				cmdlist += formathelp(helplist.admin, msg, lang);
 				cmdlist += '\n\n🔸 ' + lang.get('help.adminfooter');
-				if ( process.env.dashboard ) cmdlist += '\n\t\t' + new URL(( msg.channel.isGuild() ? `/guild/${msg.guildId}/settings` : '/' ), process.env.dashboard).href;
+				if ( process.env.dashboard ) cmdlist += '\n\t\t' + new URL(( msg.inGuild() ? `/guild/${msg.guildId}/settings` : '/' ), process.env.dashboard).href;
 				Util.splitMessage( cmdlist, {char: '\n🔹', maxLength, prepend: '🔹'} ).forEach( textpart => msg.sendChannel( textpart ) );
 			}
 			else {
@@ -119,7 +121,7 @@ function cmd_help(lang, msg, args, line, wiki) {
 		}
 		else msg.reactEmoji('❓');
 	}
-	else if ( msg.isAdmin() && pause[msg.guildId] ) {
+	else if ( msg.isAdmin() && pausedGuilds.has(msg.guildId) ) {
 		var cmdlist = lang.get('help.pause') + '\n';
 		cmdlist += formathelp(helplist.pause, msg, lang);
 		Util.splitMessage( cmdlist, {char: '\n🔹', maxLength, prepend: '🔹'} ).forEach( textpart => msg.sendChannel( textpart ) );
@@ -141,15 +143,15 @@ function cmd_help(lang, msg, args, line, wiki) {
  * Format the help messages.
  * @param {String[]} messages - The help messages.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  */
 function formathelp(messages, msg, lang) {
-	var prefix = ( msg.channel.isGuild() && patreons[msg.guildId] || process.env.prefix );
-	var mention = '@' + ( msg.channel.isGuild() ? msg.guild.me.displayName : msg.client.user.username );
+	var prefix = ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix );
+	var mention = '@' + ( msg.inGuild() ? msg.guild.me.displayName : msg.client.user.username );
 	return messages.filter( message => {
 		if ( restrictions.inline.includes( message ) && msg.noInline ) return false;
 		if ( !restrictions.patreon.includes( message ) ) return true;
-		return ( msg.channel.isGuild() && patreons[msg.guildId] );
+		return ( msg.inGuild() && patreonGuildsPrefix.has(msg.guildId) );
 	} ).map( message => {
 		var cmd = message.split('.')[0];
 		var intro = ( restrictions.inline.includes( message ) ? '' : prefix );
@@ -157,7 +159,7 @@ function formathelp(messages, msg, lang) {
 	} ).join('\n');
 }
 
-module.exports = {
+export default {
 	name: 'help',
 	everyone: true,
 	pause: true,

+ 9 - 7
cmds/link.js

@@ -1,16 +1,18 @@
+import help_setup from '../functions/helpsetup.js';
+import phabricator from '../functions/phabricator.js';
+import check_wiki_general from './wiki/general.js';
+import check_wiki_test from './test.js';
 const check_wiki = {
-	general: require('./wiki/general.js'),
-	test: require('./test.js').run
+	general: check_wiki_general,
+	test: check_wiki_test.run
 };
-const help_setup = require('../functions/helpsetup.js');
-const phabricator = require('../functions/phabricator.js');
 
 /**
  * Processes the wiki linking command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} title - The page title.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the page.
  * @param {String} [cmd] - The command at this point.
  */
 function cmd_link(lang, msg, title, wiki, cmd = '') {
@@ -31,7 +33,7 @@ function cmd_link(lang, msg, title, wiki, cmd = '') {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'LINK',
 	everyone: true,
 	pause: false,

+ 9 - 9
cmds/minecraft/bug.js

@@ -1,11 +1,11 @@
-const {MessageEmbed} = require('discord.js');
-const {got, escapeFormatting, limitLength} = require('../../util/functions.js');
+import { MessageEmbed } from 'discord.js';
+import { got, escapeFormatting, limitLength } from '../../util/functions.js';
 
 /**
  * Sends a Minecraft issue.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../../util/wiki.js')} wiki - The wiki.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki.
  * @param {String[]} args - The command arguments.
  * @param {String} title - The page title.
  * @param {String} cmd - The command at this point.
@@ -51,7 +51,7 @@ function minecraft_bug(lang, msg, wiki, args, title, cmd, reaction, spoiler, noE
 					var description = parse_links( ( body.fields.description || '' ).replace( /\{code\}/g, '```' ) );
 					var embed = null;
 					if ( msg.showEmbed() && !noEmbed ) {
-						embed = new MessageEmbed().setAuthor( 'Mojira' ).setTitle( summary ).setURL( baseBrowseUrl + body.key ).setDescription( limitLength(description, 2000, 20) );
+						embed = new MessageEmbed().setAuthor( {name: 'Mojira'} ).setTitle( summary ).setURL( baseBrowseUrl + body.key ).setDescription( limitLength(description, 2000, 20) );
 						var links = body.fields.issuelinks.filter( link => link.outwardIssue || ( link.inwardIssue && link.type.name !== 'Duplicate' ) );
 						if ( links.length ) {
 							var linkList = lang.get('minecraft.issue_link');
@@ -65,7 +65,7 @@ function minecraft_bug(lang, msg, wiki, args, title, cmd, reaction, spoiler, noE
 								if ( embed.fields.length < 25 && ( embed.length + name.length + value.length ) < 6000 ) embed.addField( name, value );
 								else extralinks.push({name,value,inline:false});
 							} );
-							if ( extralinks.length ) embed.setFooter( lang.get('minecraft.more', extralinks.length.toLocaleString(lang.get('dateformat')), extralinks.length) );
+							if ( extralinks.length ) embed.setFooter( {text: lang.get('minecraft.more', extralinks.length.toLocaleString(lang.get('dateformat')), extralinks.length)} );
 						}
 					}
 					var status = ( body.fields.resolution ? body.fields.resolution.name : body.fields.status.name );
@@ -113,7 +113,7 @@ function minecraft_bug(lang, msg, wiki, args, title, cmd, reaction, spoiler, noE
 				else {
 					var embed = null;
 					if ( msg.showEmbed() && !noEmbed ) {
-						embed = new MessageEmbed().setAuthor( 'Mojira' ).setTitle( args.join(' ') ).setURL( uri );
+						embed = new MessageEmbed().setAuthor( {name: 'Mojira'} ).setTitle( args.join(' ') ).setURL( uri );
 						if ( body.total > 0 ) {
 							var statusList = lang.get('minecraft.status');
 							body.issues.forEach( bug => {
@@ -123,7 +123,7 @@ function minecraft_bug(lang, msg, wiki, args, title, cmd, reaction, spoiler, noE
 							} );
 							if ( body.total > 25 ) {
 								var extrabugs = body.total - 25;
-								embed.setFooter( lang.get('minecraft.more', extrabugs.toLocaleString(lang.get('dateformat')), extrabugs) );
+								embed.setFooter( {text: lang.get('minecraft.more', extrabugs.toLocaleString(lang.get('dateformat')), extrabugs)} );
 							}
 						}
 					}
@@ -158,7 +158,7 @@ function parse_links(text) {
 	return text;
 }
 
-module.exports = {
+export default {
 	name: 'bug',
 	run: minecraft_bug
 };

+ 3 - 3
cmds/minecraft/command.js

@@ -1,8 +1,8 @@
 /**
  * Processes Minecraft commands.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../../util/wiki.js')} wiki - The wiki.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki.
  * @param {String[]} args - The command arguments.
  * @param {String} title - The page title.
  * @param {String} cmd - The command at this point.
@@ -21,7 +21,7 @@ function minecraft_command(lang, msg, wiki, args, title, cmd, reaction, spoiler,
 	}
 }
 
-module.exports = {
+export default {
 	name: 'command',
 	run: minecraft_command
 };

+ 9 - 7
cmds/minecraft/syntax.js

@@ -1,13 +1,15 @@
-const {Util} = require('discord.js');
-const {got} = require('../../util/functions.js');
-const Wiki = require('../../util/wiki.js');
+import { Util } from 'discord.js';
+import { got } from '../../util/functions.js';
+import Wiki from '../../util/wiki.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const commands = require('./commands.json');
 
 /**
  * Sends a Minecraft command.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../../util/wiki.js')} wiki - The wiki.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki.
  * @param {String} mccmd - The Minecraft command argument.
  * @param {String[]} args - The command arguments.
  * @param {String} title - The page title.
@@ -39,7 +41,7 @@ function minecraft_syntax(lang, msg, wiki, mccmd, args, title, cmd, reaction, sp
 		var cmdSyntax = commands.list[aliasCmd].filter( (command, i) => ( lastIndex === -1 || cmdSyntaxMap[i][0] === lastIndex ) && cmdSyntaxMap[i][1] === matchCount ).join('\n').replaceSave( regex, '/' + mccmd );
 		got.get( wiki + ( cmdpage.endsWith( '/' ) ? 'api.php?action=query&redirects=true&converttitles=true&titles=%1F' + encodeURIComponent( cmdpage + aliasCmd ) : 'api.php?action=parse&redirects=true&prop=sections&page=' + encodeURIComponent( cmdpage ) ) + '&format=json' ).then( response => {
 			var body = response.body;
-			if ( body && body.warnings ) log_warn(body.warnings);
+			if ( body && body.warnings ) log_warning(body.warnings);
 			if ( response.statusCode !== 200 || !( body?.query?.pages || body?.parse?.sections?.length ) ) {
 				console.log( '- ' + response.statusCode + ': Error while getting the command page: ' + ( body && body.error && body.error.info ) );
 			}
@@ -79,7 +81,7 @@ function minecraft_syntax(lang, msg, wiki, mccmd, args, title, cmd, reaction, sp
 	}
 }
 
-module.exports = {
+export default {
 	name: 'SYNTAX',
 	run: minecraft_syntax
 };

+ 18 - 15
cmds/patreon.js

@@ -1,18 +1,21 @@
-const {ShardClientUtil: {shardIdForGuildId}} = require('discord.js');
+import { ShardClientUtil } from 'discord.js';
+import db from '../util/database.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultPermissions, limit: {verification: verificationLimit, rcgcdw: rcgcdwLimit}} = require('../util/default.json');
-var db = require('../util/database.js');
+const {shardIdForGuildId} = ShardClientUtil;
 
 /**
  * Processes the "patreon" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_patreon(lang, msg, args, line, wiki) {
 	if ( !( process.env.channel.split('|').includes( msg.channelId ) && args.join('') ) ) {
-		if ( !msg.channel.isGuild() || !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+		if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 		return;
 	}
 	
@@ -31,7 +34,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 			});
 			return msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.\n<' + invite + '>', true );
 		}
-		if ( patreons[args[1]] ) return msg.replyMsg( '"' + guild + '" has the patreon features already enabled.', true );
+		if ( patreonGuildsPrefix.has(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]}) => {
 			if ( !row ) return msg.replyMsg( 'You can\'t have any servers.', true );
 			if ( row.count <= row.guilds ) return msg.replyMsg( 'You already reached your maximal server count.', true );
@@ -40,7 +43,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				if ( !rowCount ) return db.query( 'INSERT INTO discord(main, guild, patreon) VALUES($1, $1, $2)', [args[1], msg.author.id] ).then( () => {
 					console.log( '- Guild successfully added.' );
 					msg.client.shard.broadcastEval( (discordClient, evalData) => {
-						global.patreons[evalData.guild] = evalData.prefix;
+						patreonGuildsPrefix.set(evalData.guild, evalData.prefix);
 					}, {context: {guild: args[1], prefix: process.env.prefix}} );
 					msg.replyMsg( 'The patreon features are now enabled on "' + guild + '".', true );
 				}, dberror => {
@@ -49,7 +52,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				} );
 				console.log( '- Guild successfully updated.' );
 				msg.client.shard.broadcastEval( (discordClient, evalData) => {
-					global.patreons[evalData.guild] = evalData.prefix;
+					patreonGuildsPrefix.set(evalData.guild, evalData.prefix);
 				}, {context: {guild: args[1], prefix: process.env.prefix}} );
 				msg.replyMsg( 'The patreon features are now enabled on "' + guild + '".', true );
 			}, dberror => {
@@ -69,7 +72,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 		shard: shardIdForGuildId(args[1], msg.client.shard.count)
 	} ).then( guild => {
 		if ( !guild ) return msg.replyMsg( 'I\'m not on a server with the id `' + args[1] + '`.', true );
-		if ( !patreons[args[1]] ) return msg.replyMsg( '"' + guild + '" doesn\'t have the patreon features enabled.', true );
+		if ( !patreonGuildsPrefix.has(args[1]) ) return msg.replyMsg( '"' + guild + '" doesn\'t have the patreon features enabled.', true );
 		return db.connect().then( client => {
 			return client.query( 'SELECT lang, role, inline FROM discord WHERE guild = $1 AND patreon = $2', [args[1], msg.author.id] ).then( ({rows:[row]}) => {
 				if ( !row ) {
@@ -83,7 +86,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				return client.query( 'UPDATE discord SET lang = $1, role = $2, inline = $3, prefix = $4, patreon = NULL WHERE guild = $5', [row.lang, row.role, row.inline, process.env.prefix, args[1]] ).then( () => {
 					console.log( '- Guild successfully updated.' );
 					msg.client.shard.broadcastEval( (discordClient, evalData) => {
-						delete global.patreons[evalData];
+						patreonGuildsPrefix.delete(evalData);
 					}, {context: args[1]} );
 					msg.replyMsg( 'The patreon features are now disabled on "' + guild + '".', true );
 				}, dberror => {
@@ -97,7 +100,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 							return msg.client.shard.broadcastEval( (discordClient, evalData) => {
 								if ( discordClient.guilds.cache.has(evalData.guild) ) {
 									return discordClient.guilds.cache.get(evalData.guild).channels.cache.filter( channel => {
-										return ( channel.isGuild(false) && evalData.rows.some( row => {
+										return ( ( channel.isText() && !channel.isThread() ) && evalData.rows.some( row => {
 											return ( row.channel === '#' + channel.parentId );
 										} ) );
 									} ).map( channel => {
@@ -237,7 +240,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				if ( !guilds.length ) return Promise.reject();
 				msg.client.shard.broadcastEval( (discordClient, evalData) => {
 					return evalData.map( guild => {
-						delete global.patreons[guild];
+						patreonGuildsPrefix.delete(guild);
 					} );
 				}, {context: row.guilds} );
 			}, dberror => {
@@ -265,7 +268,7 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 								return discordClient.guilds.cache.has(guild);
 							} ).map( guild => {
 								return discordClient.guilds.cache.get(guild).channels.cache.filter( channel => {
-									return ( channel.isGuild(false) && evalData.rows.some( row => {
+									return ( ( channel.isText() && !channel.isThread() ) && evalData.rows.some( row => {
 										return ( row.channel === '#' + channel.parentId );
 									} ) );
 								} ).map( channel => {
@@ -361,10 +364,10 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 		msg.replyMsg( 'I got an error while searching for <@' + args[1] + '>, please try again later.', true );
 	} );
 	
-	if ( !msg.channel.isGuild() || !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+	if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 }
 
-module.exports = {
+export default {
 	name: 'patreon',
 	everyone: true,
 	pause: true,

+ 8 - 8
cmds/pause.js

@@ -1,28 +1,28 @@
 /**
  * Processes the "pause" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_pause(lang, msg, args, line, wiki) {
-	if ( msg.channel.isGuild() && args.join(' ').split('\n')[0].isMention(msg.guild) && ( msg.isAdmin() || msg.isOwner() ) ) {
-		if ( pause[msg.guildId] ) {
-			delete pause[msg.guildId];
+	if ( msg.inGuild() && args.join(' ').split('\n')[0].isMention(msg.guild) && ( msg.isAdmin() || msg.isOwner() ) ) {
+		if ( pausedGuilds.has(msg.guildId) ) {
+			pausedGuilds.delete(msg.guildId);
 			console.log( '- Pause ended.' );
 			msg.replyMsg( lang.get('pause.off'), true );
 		} else {
 			msg.replyMsg( lang.get('pause.on'), true );
 			console.log( '- Pause started.' );
-			pause[msg.guildId] = true;
+			pausedGuilds.add(msg.guildId);
 		}
-	} else if ( !msg.channel.isGuild() || !pause[msg.guildId] ) {
+	} else if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) {
 		this.LINK(lang, msg, line, wiki);
 	}
 }
 
-module.exports = {
+export default {
 	name: 'pause',
 	everyone: true,
 	pause: true,

+ 22 - 20
cmds/rcscript.js

@@ -1,15 +1,17 @@
-const cheerio = require('cheerio');
-const {Util, MessageActionRow, MessageButton, Permissions: {FLAGS}} = require('discord.js');
-const help_setup = require('../functions/helpsetup.js');
+import { existsSync } from 'fs';
+import cheerio from 'cheerio';
+import { Util, MessageActionRow, MessageButton, Permissions } from 'discord.js';
+import help_setup from '../functions/helpsetup.js';
+import { got } from '../util/functions.js';
+import Lang from '../util/i18n.js';
+import Wiki from '../util/wiki.js';
+import db from '../util/database.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {rcgcdw: rcgcdwLimit}} = require('../util/default.json');
-const {got} = require('../util/functions.js');
-const Lang = require('../util/i18n.js');
 const allLangs = Lang.allLangs(true);
-const Wiki = require('../util/wiki.js');
-var db = require('../util/database.js');
 
-const fs = require('fs');
-const rcscriptExists = ( isDebug || fs.existsSync('./RcGcDb/start.py') );
+const rcscriptExists = ( isDebug || existsSync('./RcGcDb/start.py') );
 
 const display_types = [
 	'compact',
@@ -35,8 +37,8 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 		var prefix = process.env.prefix;
 		var limit = rcgcdwLimit.default;
 		var display = display_types.slice(0, rcgcdwLimit.display + 1);
-		if ( patreons[msg.guildId] ) {
-			prefix = patreons[msg.guildId];
+		if ( patreonGuildsPrefix.has(msg.guildId) ) {
+			prefix = patreonGuildsPrefix.get(msg.guildId);
 			limit = rcgcdwLimit.patreon;
 			display = display_types.slice();
 		}
@@ -48,11 +50,11 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 		}
 
 		if ( args[0] === 'add' ) {
-			if ( !msg.channel.permissionsFor(msg.client.user).has(FLAGS.MANAGE_WEBHOOKS) ) {
+			if ( !msg.channel.permissionsFor(msg.client.user).has(Permissions.FLAGS.MANAGE_WEBHOOKS) ) {
 				console.log( msg.guildId + ': Missing permissions - MANAGE_WEBHOOKS' );
 				return msg.replyMsg( lang.get('general.missingperm') + ' `MANAGE_WEBHOOKS`' );
 			}
-			if ( !( msg.channel.permissionsFor(msg.member).has(FLAGS.MANAGE_WEBHOOKS) || ( msg.isOwner() && msg.evalUsed ) ) ) {
+			if ( !( msg.channel.permissionsFor(msg.member).has(Permissions.FLAGS.MANAGE_WEBHOOKS) || ( msg.isOwner() && msg.evalUsed ) ) ) {
 				return msg.replyMsg( lang.get('rcscript.noadmin') );
 			}
 			if ( rows.length >= limit ) return msg.replyMsg( lang.get('rcscript.max_entries'), true );
@@ -196,7 +198,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, true );
 				return msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
 					var channel = msg.guild.channels.cache.get(webhook.channelId);
-					if ( !channel || !channel.permissionsFor(msg.member).has(FLAGS.MANAGE_WEBHOOKS) ) {
+					if ( !channel || !channel.permissionsFor(msg.member).has(Permissions.FLAGS.MANAGE_WEBHOOKS) ) {
 						return msg.replyMsg( lang.get('rcscript.noadmin') );
 					}
 					db.query( 'DELETE FROM rcgcdw WHERE webhook = $1', [selected_row.webhook] ).then( () => {
@@ -212,7 +214,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 					} );
 				}, error => {
 					log_error(error);
-					if ( error.name === 'DiscordAPIError' && ['Unknown Webhook', 'Invalid Webhook Token'].includes( error.message ) ) {
+					if ( error.name !== 'DiscordAPIError' || !['Unknown Webhook', 'Invalid Webhook Token'].includes( error.message ) ) {
 						button?.setURL(new URL(`/guild/${msg.guildId}/rcscript/${selected_row.configid}`, button.url).href);
 						return msg.replyMsg( {content: lang.get('settings.save_failed'), components}, true );
 					}
@@ -505,9 +507,9 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 			} );
 			rows = rows.filter( row => row.channel );
 			var only = ( rows.length === 1 );
-			var text = '';
+			var text = null;
 			if ( rows.length ) {
-				text += lang.get('rcscript.current');
+				text = lang.get('rcscript.current');
 				if ( button ) text += `\n<${button.url}>`;
 				text += rows.map( row => {
 					var cmd = prefix + 'rcscript' + ( only ? '' : ' ' + row.configid );
@@ -534,7 +536,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
 				} ).join('');
 			}
 			else {
-				text += lang.get('rcscript.missing');
+				text = lang.get('rcscript.missing');
 				if ( button ) 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') + '`';
@@ -552,7 +554,7 @@ function cmd_rcscript(lang, msg, args, line, wiki) {
  * @param {String[]} args - The command arguments.
  */
 function blocklist(msg, args) {
-	var prefix = ( patreons[msg?.guildId] || process.env.prefix );
+	var prefix = ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix );
 	if ( args[0] === 'add' ) {
 		if ( !args[1] ) return msg.replyMsg( '`' + prefix + 'rcscript block add <wiki> [<reason>]`', true );
 		if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, true );
@@ -622,7 +624,7 @@ function blocklist(msg, args) {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'rcscript',
 	everyone: rcscriptExists,
 	pause: rcscriptExists,

+ 16 - 9
cmds/say.js

@@ -1,12 +1,12 @@
-const {Permissions: {FLAGS}} = require('discord.js');
+import { Permissions } from 'discord.js';
 
 /**
  * Processes the "say" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_say(lang, msg, args, line, wiki) {
 	var text = args.join(' ');
@@ -22,17 +22,24 @@ function cmd_say(lang, msg, args, line, wiki) {
 		}
 	}
 	if ( text.trim() || imgs.length ) {
-		var allowedMentions = {parse:['users']};
-		if ( msg.member.permissions.has(FLAGS.MENTION_EVERYONE) ) allowedMentions.parse = ['users','roles','everyone'];
-		else allowedMentions.roles = msg.guild.roles.cache.filter( role => role.mentionable ).map( role => role.id ).slice(0,100)
-		msg.channel.send( {content: text, allowedMentions, files: imgs} ).then( () => msg.delete().catch(log_error), error => {
+		let allowedMentions = {parse:['users']};
+		if ( msg.member.permissions.has(Permissions.FLAGS.MENTION_EVERYONE) ) allowedMentions.parse = ['users','roles','everyone'];
+		else allowedMentions.roles = msg.guild.roles.cache.filter( role => role.mentionable ).map( role => role.id ).slice(0, 100);
+		msg.channel.send( {
+			content: text,
+			files: imgs,
+			allowedMentions,
+			reply: {
+				messageReference: msg.reference?.messageId
+			}
+		} ).then( () => msg.delete().catch(log_error), error => {
 			log_error(error);
 			msg.reactEmoji('error', true);
 		} );
-	} else if ( !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+	} else if ( !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 }
 
-module.exports = {
+export default {
 	name: 'say',
 	everyone: false,
 	pause: false,

+ 20 - 18
cmds/settings.js

@@ -1,11 +1,13 @@
-const cheerio = require('cheerio');
-const {MessageEmbed, Util, MessageActionRow, MessageButton} = require('discord.js');
+import cheerio from 'cheerio';
+import { MessageEmbed, Util, MessageActionRow, MessageButton } from 'discord.js';
+import { got } from '../util/functions.js';
+import Lang from '../util/i18n.js';
+import Wiki from '../util/wiki.js';
+import db from '../util/database.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultSettings} = require('../util/default.json');
-const {got} = require('../util/functions.js');
-const Lang = require('../util/i18n.js');
 const allLangs = Lang.allLangs();
-const Wiki = require('../util/wiki.js');
-var db = require('../util/database.js');
 
 /**
  * Processes the "settings" command.
@@ -37,13 +39,13 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			text = lang.get('settings.current');
 			if ( button ) text += `\n<${button.url}>`;
 			text += '\n' + lang.get('settings.currentlang') + ' `' + allLangs.names[guild.lang] + '` - `' + prefix + 'settings lang`';
-			if ( patreons[msg.guildId] ) text += '\n' + lang.get('settings.currentprefix') + ' `' + prefix + '` - `' + prefix + 'settings prefix`';
+			if ( patreonGuildsPrefix.has(msg.guildId) ) text += '\n' + lang.get('settings.currentprefix') + ' `' + prefix + '` - `' + prefix + 'settings prefix`';
 			text += '\n' + lang.get('settings.currentrole') + ' ' + ( guild.role ? `<@&${guild.role}>` : '@everyone' ) + ' - `' + prefix + 'settings role`';
 			text += '\n' + lang.get('settings.currentinline') + ' ' + ( guild.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( guild.inline ? '~~' : '' ) + ' - `' + prefix + 'settings inline`';
 			text += '\n' + lang.get('settings.currentwiki') + ' ' + guild.wiki + ' - `' + prefix + 'settings wiki`';
 			text += '\n' + lang.get('settings.currentchannel') + ' `' + prefix + 'settings channel`\n';
 			if ( rows.length === 1 ) text += lang.get('settings.nochannels');
-			else text += rows.filter( row => row !== guild ).map( row => '<#' + row.channel.replace( /^#/, '' ) + '>: ' + ( patreons[msg.guildId] ? '`' + allLangs.names[row.lang] + '` - ' : '' ) + '<' + row.wiki + '>' + ( patreons[msg.guildId] ? ' - ' + ( row.role ? `<@&${row.role}>` : '@everyone' ) + ' - ' + ( row.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( row.inline ? '~~' : '' ) : '' ) ).join('\n');
+			else text += rows.filter( row => row !== guild ).map( row => '<#' + row.channel.replace( /^#/, '' ) + '>: ' + ( patreonGuildsPrefix.has(msg.guildId) ? '`' + allLangs.names[row.lang] + '` - ' : '' ) + '<' + row.wiki + '>' + ( patreonGuildsPrefix.has(msg.guildId) ? ' - ' + ( row.role ? `<@&${row.role}>` : '@everyone' ) + ' - ' + ( row.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( row.inline ? '~~' : '' ) : '' ) ).join('\n');
 		}
 		
 		if ( !args.length ) {
@@ -64,7 +66,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			text = lang.get('settings.channel current');
 			button?.setURL(new URL(`/guild/${msg.guildId}/settings/${channelId}`, button.url).href);
 			if ( button ) text += `\n<${button.url}>`;
-			if ( patreons[msg.guildId] ) {
+			if ( patreonGuildsPrefix.has(msg.guildId) ) {
 				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`';
 				text += '\n' + lang.get('settings.currentinline') + ' ' + ( channel.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( channel.inline ? '~~' : '' ) + ' - `' + prefix + 'settings channel inline`';
@@ -132,7 +134,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 							});
 						}
 						if ( notice.length ) {
-							embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( lang.get('test.notice') ).addFields( notice );
+							embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( lang.get('test.notice') ).addFields( notice );
 						}
 					}
 					var sql = 'UPDATE discord SET wiki = $1 WHERE guild = $2 AND wiki = $3';
@@ -193,7 +195,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}
 		
 		if ( args[0] === 'lang' || args[0] === 'language' ) {
-			if ( channel && !patreons[msg.guildId] ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
+			if ( channel && !patreonGuildsPrefix.has(msg.guildId) ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
 			prelang += 'lang';
 			var langhelp = '\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`';
 			if ( !args[1] ) {
@@ -228,9 +230,9 @@ function cmd_settings(lang, msg, args, line, wiki) {
 						if ( row.channel && row.lang === guild.lang ) row.lang = allLangs.map[args[1]];
 					} );
 					guild.lang = allLangs.map[args[1]];
-					if ( voice[msg.guildId] ) voice[msg.guildId] = guild.lang;
+					if ( voiceGuildsLang.has(msg.guildId) ) voiceGuildsLang.set(msg.guildId, guild.lang);
 				}
-				if ( channel || !patreons[msg.guildId] || !rows.some( row => row.channel === channelId ) ) lang = new Lang(allLangs.map[args[1]]);
+				if ( channel || !patreonGuildsPrefix.has(msg.guildId) || !rows.some( row => row.channel === channelId ) ) lang = new Lang(allLangs.map[args[1]]);
 				msg.replyMsg( {content: 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( () => {
@@ -245,7 +247,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}
 		
 		if ( args[0] === 'role' ) {
-			if ( channel && !patreons[msg.guildId] ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
+			if ( channel && !patreonGuildsPrefix.has(msg.guildId) ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
 			prelang += 'role';
 			var rolehelp = '\n' + lang.get('settings.rolehelp', prefix + 'settings ' + prelang);
 			if ( !args[1] ) {
@@ -307,7 +309,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}
 		
 		if ( args[0] === 'prefix' && !channel ) {
-			if ( !patreons[msg.guildId] ) {
+			if ( !patreonGuildsPrefix.has(msg.guildId) ) {
 				return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
 			}
 			var prefixhelp = '\n' + lang.get('settings.prefixhelp', prefix + 'settings prefix');
@@ -330,7 +332,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 				console.log( '- Settings successfully updated.' );
 				guild.prefix = args[1];
 				msg.client.shard.broadcastEval( (discordClient, evalData) => {
-					global.patreons[evalData.guild] = evalData.prefix;
+					patreonGuildsPrefix.set(evalData.guild, evalData.prefix);
 				}, {context: {guild: msg.guildId, prefix: args[1]}} );
 				msg.replyMsg( {content: lang.get('settings.prefixchanged') + ' `' + args[1] + '`\n' + lang.get('settings.prefixhelp', args[1] + 'settings prefix'), components}, true );
 			}, dberror => {
@@ -340,7 +342,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 		}
 		
 		if ( args[0] === 'inline' ) {
-			if ( channel && !patreons[msg.guildId] ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
+			if ( channel && !patreonGuildsPrefix.has(msg.guildId) ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', true );
 			prelang += 'inline';
 			var toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
 			var inlinehelp = '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage);
@@ -393,7 +395,7 @@ function cmd_settings(lang, msg, args, line, wiki) {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'settings',
 	everyone: true,
 	pause: true,

+ 9 - 5
cmds/stop.js

@@ -1,10 +1,10 @@
 /**
  * Processes the "stop" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  * @async
  */
 async function cmd_stop(lang, msg, args, line, wiki) {
@@ -14,13 +14,17 @@ async function cmd_stop(lang, msg, args, line, wiki) {
 	} else if ( args.join(' ').split('\n')[0].isMention(msg.guild) ) {
 		await msg.replyMsg( 'I\'ll restart myself now!', true );
 		console.log( '\n- Restarting all shards!\n\n' );
-		await msg.client.shard.respawnAll({timeout: -1});
-	} else if ( !msg.channel.isGuild() || !pause[msg.guildId] ) {
+		await msg.client.shard.respawnAll( {
+			shardDelay: 5_000,
+			respawnDelay: 500,
+			timeout: 60_000
+		} );
+	} else if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) {
 		this.LINK(lang, msg, line, wiki);
 	}
 }
 
-module.exports = {
+export default {
 	name: 'stop',
 	everyone: false,
 	pause: false,

+ 21 - 19
cmds/test.js

@@ -1,7 +1,7 @@
-const {MessageEmbed} = require('discord.js');
-const help_setup = require('../functions/helpsetup.js');
-const {got} = require('../util/functions.js');
-const logging = require('../util/logging.js');
+import { MessageEmbed } from 'discord.js';
+import help_setup from '../functions/helpsetup.js';
+import { got } from '../util/functions.js';
+import logging from '../util/logging.js';
 
 const wsStatus = [
 	'READY',
@@ -17,34 +17,36 @@ const wsStatus = [
 
 /**
  * Processes the "test" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_test(lang, msg, args, line, wiki) {
 	if ( args.join('') ) {
-		if ( !msg.channel.isGuild() || !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+		if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 	}
-	else if ( !msg.channel.isGuild() || !pause[msg.guildId] ) {
+	else if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) {
 		if ( msg.isAdmin() && msg.defaultSettings ) help_setup(lang, msg);
 		let textList = lang.get('test.text').filter( text => text.trim() );
 		var text = ( textList[Math.floor(Math.random() * ( textList.length * 5 ))] || lang.get('test.text.0') );
 		if ( process.env.READONLY ) text = lang.get('general.readonly') + '\n' + process.env.invite;
 		console.log( '- Test[' + process.env.SHARDS + ']: Fully functioning!' );
-		var now = Date.now();
 		msg.replyMsg( text ).then( message => {
 			if ( !message ) return;
-			var then = Date.now();
-			var embed = new MessageEmbed().setTitle( lang.get('test.time') ).setFooter( 'Shard: ' + process.env.SHARDS ).addField( 'Discord', ( then - now ).toLocaleString(lang.get('dateformat')) + 'ms' );
-			now = Date.now();
+			var discordPing = message.createdTimestamp - msg.createdTimestamp;
+			if ( discordPing > 1_000 ) text = lang.get('test.slow') + ' 🐌\n' + process.env.invite;
+			var embed = new MessageEmbed().setTitle( lang.get('test.time') ).setFooter( {text: 'Shard: ' + process.env.SHARDS} ).addField( 'Discord', discordPing.toLocaleString(lang.get('dateformat')) + 'ms' );
+			var now = Date.now();
 			got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&format=json', {
-				timeout: 10000
+				timeout: {
+					request: 10_000
+				}
 			} ).then( response => {
-				then = Date.now();
+				var then = Date.now();
 				var body = response.body;
-				if ( body && body.warnings ) log_warn(body.warnings);
+				if ( body && body.warnings ) log_warning(body.warnings);
 				var ping = ( then - now ).toLocaleString(lang.get('dateformat')) + 'ms';
 				if ( body?.query?.general ) wiki.updateWiki(body.query.general);
 				embed.addField( wiki.toLink(), ping );
@@ -69,7 +71,7 @@ function cmd_test(lang, msg, args, line, wiki) {
 				else logging(wiki, msg.guildId, 'test');
 				if ( notice.length ) embed.addField( lang.get('test.notice'), notice.join('\n') );
 			}, error => {
-				then = Date.now();
+				var then = Date.now();
 				var ping = ( then - now ).toLocaleString(lang.get('dateformat')) + 'ms';
 				if ( wiki.noWiki(error.message) ) {
 					console.log( '- This wiki doesn\'t exist!' );
@@ -87,9 +89,9 @@ function cmd_test(lang, msg, args, line, wiki) {
 					return '```js\n' + error + '\n```';
 				} ).then( shards => {
 					embed.addField( 'Shards', shards );
-					message.edit( {content: message.content, embeds: [embed]} ).catch(log_error);
+					message.edit( {content: text, embeds: [embed]} ).catch(log_error);
 				} );
-				message.edit( {content: message.content, embeds: [embed]} ).catch(log_error);
+				message.edit( {content: text, embeds: [embed]} ).catch(log_error);
 			} );
 		} );
 	}
@@ -99,7 +101,7 @@ function cmd_test(lang, msg, args, line, wiki) {
 	}
 }
 
-module.exports = {
+export default {
 	name: 'test',
 	everyone: true,
 	pause: true,

+ 24 - 22
cmds/verification.js

@@ -1,31 +1,33 @@
-const {Util, MessageActionRow, MessageButton, Permissions: {FLAGS}} = require('discord.js');
-const help_setup = require('../functions/helpsetup.js');
+import { Util, MessageActionRow, MessageButton, Permissions } from 'discord.js';
+import help_setup from '../functions/helpsetup.js';
+import db from '../util/database.js';
+import { got } from '../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {verification: verificationLimit}} = require('../util/default.json');
-var db = require('../util/database.js');
-const {got} = require('../util/functions.js');
 
 /**
  * Processes the "verification" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_verification(lang, msg, args, line, wiki) {
 	if ( !msg.isAdmin() ) {
-		if ( msg.channel.isGuild() && !pause[msg.guildId] ) this.verify(lang, msg, args, line, wiki);
+		if ( msg.inGuild() && !pausedGuilds.has(msg.guildId) ) this.verify(lang, msg, args, line, wiki);
 		else msg.reactEmoji('❌');
 		return;
 	}
 	if ( msg.defaultSettings ) return help_setup(lang, msg);
-	if ( !msg.guild.me.permissions.has(FLAGS.MANAGE_ROLES) ) {
+	if ( !msg.guild.me.permissions.has(Permissions.FLAGS.MANAGE_ROLES) ) {
 		console.log( msg.guildId + ': Missing permissions - MANAGE_ROLES' );
 		return msg.replyMsg( lang.get('general.missingperm') + ' `MANAGE_ROLES`' );
 	}
 	
 	db.query( 'SELECT configid, channel, role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 ORDER BY configid ASC', [msg.guildId] ).then( ({rows}) => {
-		var prefix = ( patreons[msg.guildId] || process.env.prefix );
+		var prefix = ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix );
 		var button = null;
 		var components = [];
 		if ( process.env.dashboard ) {
@@ -33,7 +35,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			components.push(new MessageActionRow().addComponents(button));
 		}
 		if ( args[0] && args[0].toLowerCase() === 'add' ) {
-			var limit = verificationLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+			var limit = verificationLimit[( patreonGuildsPrefix.has(msg.guildId) ? '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?.setURL(new URL(`/guild/${msg.guildId}/verification/new`, button.url).href);
@@ -81,17 +83,17 @@ function cmd_verification(lang, msg, args, line, wiki) {
 		}
 		if ( !rows.some( row => row.configid.toString() === args[0] ) ) {
 			if ( args.length ) {
-				if ( !pause[msg.guildId] ) this.verify(lang, msg, args, line, wiki);
+				if ( !pausedGuilds.has(msg.guildId) ) this.verify(lang, msg, args, line, wiki);
 				return;
 			}
-			var text = '';
+			var text = null;
 			if ( rows.length ) {
-				text += lang.get('verification.current');
+				text = lang.get('verification.current');
 				if ( button ) text += `\n<${button.url}>`;
 				text += rows.map( row => formatVerification(false, true, row) ).join('');
 			}
 			else {
-				text += lang.get('verification.missing');
+				text = lang.get('verification.missing');
 				if ( button ) text += `\n<${button.url}>`;
 			}
 			text += '\n\n' + lang.get('verification.add_more') + '\n`' + prefix + 'verification add ' + lang.get('verification.new_role') + '`';
@@ -120,7 +122,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 		}
 		button?.setURL(new URL(`/guild/${msg.guildId}/verification/${row.configid}`, button.url).href);
 		if ( args[1] === 'rename' && !args.slice(2).join('') ) {
-			if ( !row.rename && !msg.guild.me.permissions.has(FLAGS.MANAGE_NICKNAMES) ) {
+			if ( !row.rename && !msg.guild.me.permissions.has(Permissions.FLAGS.MANAGE_NICKNAMES) ) {
 				console.log( msg.guildId + ': Missing permissions - MANAGE_NICKNAMES' );
 				return msg.replyMsg( lang.get('general.missingperm') + ' `MANAGE_NICKNAMES`' );
 			}
@@ -142,9 +144,9 @@ function cmd_verification(lang, msg, args, line, wiki) {
 				if ( channels.length > 10 ) return msg.replyMsg( {content: 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(false) ).get(channel);
-					if ( !new_channel ) new_channel = msg.guild.channels.cache.filter( gc => gc.isGuild(false) ).find( gc => gc.name === channel.replace( /^#/, '' ) );
-					if ( !new_channel ) new_channel = msg.guild.channels.cache.filter( gc => gc.isGuild(false) ).find( gc => gc.name.toLowerCase() === channel.toLowerCase().replace( /^#/, '' ) );
+					if ( /^\d+$/.test(channel) ) new_channel = msg.guild.channels.cache.filter( gc => gc.isText() && !gc.isThread() ).get(channel);
+					if ( !new_channel ) new_channel = msg.guild.channels.cache.filter( gc => gc.isText() && !gc.isThread() ).find( gc => gc.name === channel.replace( /^#/, '' ) );
+					if ( !new_channel ) new_channel = msg.guild.channels.cache.filter( gc => gc.isText() && !gc.isThread() ).find( gc => gc.name.toLowerCase() === channel.toLowerCase().replace( /^#/, '' ) );
 					return new_channel;
 				} );
 				if ( channels.some( channel => !channel ) ) return msg.replyMsg( {content: lang.get('verification.channel_missing'), components}, true );
@@ -187,7 +189,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			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 ) {
+				if ( args[2] > 1_000_000 || args[2] < -1_000_000 ) {
 					return msg.replyMsg( {content: 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.guildId, row.configid] ).then( () => {
@@ -210,7 +212,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 				if ( usergroups.some( usergroup => usergroup.length > 100 ) ) return msg.replyMsg( {content: 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);
+					if ( body && body.warnings ) log_warning(body.warnings);
 					if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.allmessages ) {
 						if ( wiki.noWiki(response.url, response.statusCode) ) console.log( '- This wiki doesn\'t exist!' );
 						else console.log( '- ' + response.statusCode + ': Error while getting the usergroups: ' + ( body && body.error && body.error.info ) );
@@ -291,7 +293,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 			if ( showCommands ) verification_text += '\n`' + prefix + 'verification ' + row.configid + ' accountage ' + lang.get('verification.new_accountage') + '`\n';
 			verification_text += '\n' + lang.get('verification.rename') + ' *`' + lang.get('verification.' + ( rename ? 'enabled' : 'disabled')) + '`*';
 			if ( showCommands ) verification_text += ' ' + lang.get('verification.toggle') + '\n`' + prefix + 'verification ' + row.configid + ' rename`\n';
-			if ( !hideNotice && rename && !msg.guild.me.permissions.has(FLAGS.MANAGE_NICKNAMES) ) {
+			if ( !hideNotice && rename && !msg.guild.me.permissions.has(Permissions.FLAGS.MANAGE_NICKNAMES) ) {
 				verification_text += '\n\n' + lang.get('verification.rename_no_permission', msg.guild.me.toString());
 			}
 			if ( !hideNotice && role.replace( /-/g, '' ).split('|').some( role => {
@@ -315,7 +317,7 @@ function cmd_verification(lang, msg, args, line, wiki) {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'verification',
 	everyone: true,
 	pause: true,

+ 25 - 21
cmds/verify.js

@@ -1,20 +1,20 @@
-const {randomBytes} = require('crypto');
-const {MessageEmbed, MessageActionRow, MessageButton, Permissions: {FLAGS}} = require('discord.js');
-var db = require('../util/database.js');
-var verify = require('../functions/verify.js');
-const {got, oauthVerify, allowDelete, escapeFormatting} = require('../util/functions.js');
+import { randomBytes } from 'crypto';
+import { MessageEmbed, MessageActionRow, MessageButton, Permissions } from 'discord.js';
+import db from '../util/database.js';
+import verify from '../functions/verify.js';
+import { got, oauthVerify, allowDelete, escapeFormatting } from '../util/functions.js';
 
 /**
  * Processes the "verify" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_verify(lang, msg, args, line, wiki) {
-	if ( !msg.channel.isGuild() || msg.defaultSettings ) return this.LINK(lang, msg, line, wiki);
-	if ( !msg.guild.me.permissions.has(FLAGS.MANAGE_ROLES) ) {
+	if ( !msg.inGuild() || msg.defaultSettings ) return this.LINK(lang, msg, line, wiki);
+	if ( !msg.guild.me.permissions.has(Permissions.FLAGS.MANAGE_ROLES) ) {
 		if ( msg.isAdmin() ) {
 			console.log( msg.guildId + ': Missing permissions - MANAGE_ROLES' );
 			msg.replyMsg( lang.get('general.missingperm') + ' `MANAGE_ROLES`' );
@@ -26,7 +26,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 	db.query( 'SELECT logchannel, flags, onsuccess, onmatch, role, editcount, postcount, usergroup, accountage, rename FROM verification LEFT JOIN verifynotice ON verification.guild = verifynotice.guild WHERE verification.guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [msg.guildId, '%|' + ( msg.channel.isThread() ? msg.channel.parentId : msg.channelId ) + '|%'] ).then( ({rows}) => {
 		if ( !rows.length ) {
 			if ( msg.onlyVerifyCommand ) return;
-			return msg.replyMsg( lang.get('verify.missing') + ( msg.isAdmin() ? '\n`' + ( patreons[msg.guildId] || process.env.prefix ) + 'verification`' : '' ) );
+			return msg.replyMsg( lang.get('verify.missing') + ( msg.isAdmin() ? '\n`' + ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix ) + 'verification`' : '' ) );
 		}
 		
 		if ( wiki.hasOAuth2() && process.env.dashboard ) {
@@ -53,7 +53,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 						}, dberror => {
 							console.log( '- Dashboard: Error while updating the OAuth2 token for ' + msg.author.id + ': ' + dberror );
 						} );
-						return global.verifyOauthUser('', body.access_token, {
+						return verifyOauthUser('', body.access_token, {
 							wiki: wiki.href, channel: msg.channel,
 							user: msg.author.id, sourceMessage: msg,
 							fail: () => msg.replyMsg( lang.get('verify.error_reply'), false, false ).then( message => {
@@ -99,7 +99,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 					} ).then( message => {
 						msg.reactEmoji('📩');
 						allowDelete(message, msg.author.id);
-						setTimeout( () => msg.delete().catch(log_error), 60000 ).unref();
+						setTimeout( () => msg.delete().catch(log_error), 60_000 ).unref();
 					}, error => {
 						if ( error?.code === 50007 ) { // CANNOT_MESSAGE_USER
 							return msg.replyMsg( lang.get('verify.oauth_private') );
@@ -144,7 +144,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 							}, dberror => {
 								console.log( '- Dashboard: Error while updating the OAuth2 token for ' + msg.author.id + ': ' + dberror );
 							} );
-							return global.verifyOauthUser('', body.access_token, {
+							return verifyOauthUser('', body.access_token, {
 								wiki: wiki.href, channel: msg.channel,
 								user: msg.author.id, sourceMessage: msg,
 								fail: () => msg.replyMsg( lang.get('verify.error_reply'), false, false ).then( message => {
@@ -190,7 +190,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 						} ).then( message => {
 							msg.reactEmoji('📩');
 							allowDelete(message, msg.author.id);
-							setTimeout( () => msg.delete().catch(log_error), 60000 ).unref();
+							setTimeout( () => msg.delete().catch(log_error), 60_000 ).unref();
 						}, error => {
 							if ( error?.code === 50007 ) { // CANNOT_MESSAGE_USER
 								return msg.replyMsg( lang.get('verify.oauth_private') );
@@ -228,20 +228,24 @@ function cmd_verify(lang, msg, args, line, wiki) {
 						msg.member.send( {content: msg.channel.toString() + '; ' + result.content, embeds: dmEmbeds, components: []} ).then( message => {
 							msg.reactEmoji('📩');
 							allowDelete(message, msg.author.id);
-							setTimeout( () => msg.delete().catch(log_error), 60000 ).unref();
+							setTimeout( () => msg.delete().catch(log_error), 60_000 ).unref();
 						}, error => {
 							if ( error?.code === 50007 ) { // CANNOT_MESSAGE_USER
 								return msg.replyMsg( options, false, false );
 							}
 							log_error(error);
 							msg.reactEmoji('error');
-						} );
-						if ( result.logging.channel && msg.guild.channels.cache.has(result.logging.channel) ) {
+						} ).then( message => {
+							if ( !result.logging.channel || !msg.guild.channels.cache.has(result.logging.channel) ) return;
+							if ( message ) {
+								if ( result.logging.embed ) result.logging.embed.addField(message.url, '<#' + msg.channelId + '>');
+								else result.logging.content += '\n<#' + msg.channelId + '> – <' + message.url + '>';
+							}
 							msg.guild.channels.cache.get(result.logging.channel).send( {
 								content: result.logging.content,
-								embeds: [result.logging.embed]
+								embeds: ( result.logging.embed ? [result.logging.embed] : [] )
 							} ).catch(log_error);
-						}
+						} );
 					}
 					else msg.replyMsg( options, false, false ).then( message => {
 						if ( !result.logging.channel || !msg.guild.channels.cache.has(result.logging.channel) ) return;
@@ -251,7 +255,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 						}
 						msg.guild.channels.cache.get(result.logging.channel).send( {
 							content: result.logging.content,
-							embeds: [result.logging.embed]
+							embeds: ( result.logging.embed ? [result.logging.embed] : [] )
 						} ).catch(log_error);
 					} );
 				}
@@ -271,7 +275,7 @@ function cmd_verify(lang, msg, args, line, wiki) {
 	} );
 }
 
-module.exports = {
+export default {
 	name: 'verify',
 	everyone: true,
 	pause: false,

+ 11 - 11
cmds/voice.js

@@ -1,40 +1,40 @@
-const help_setup = require('../functions/helpsetup.js');
-var db = require('../util/database.js');
+import help_setup from '../functions/helpsetup.js';
+import db from '../util/database.js';
 
 /**
  * Processes the "voice" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
  * @param {String} line - The command as plain text.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  */
 function cmd_voice(lang, msg, args, line, wiki) {
 	if ( msg.isAdmin() ) {
 		if ( !args.join('') ) {
 			var text = lang.get('voice.text') + '\n`' + lang.get('voice.channel') + ' – <' + lang.get('voice.name') + '>`\n';
-			text += lang.get('voice.' + ( voice[msg.guildId] ? 'disable' : 'enable' ), ( patreons[msg.guildId] || process.env.prefix ) + 'voice toggle');
+			text += lang.get('voice.' + ( voiceGuildsLang.has(msg.guildId) ? 'disable' : 'enable' ), ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix ) + 'voice toggle');
 			return msg.replyMsg( text, true );
 		}
 		args[1] = args.slice(1).join(' ').trim()
 		if ( args[0].toLowerCase() === 'toggle' && !args[1] ) {
 			if ( msg.defaultSettings ) return help_setup(lang, msg);
 			if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, true );
-			var value = ( voice[msg.guildId] ? null : 1 );
+			var value = ( voiceGuildsLang.has(msg.guildId) ? null : 1 );
 			return db.query( 'UPDATE discord SET voice = $1 WHERE guild = $2 AND channel IS NULL', [value, msg.guildId] ).then( () => {
 				console.log( '- Voice settings successfully updated.' );
 				if ( value ) {
-					voice[msg.guildId] = lang.lang;
+					voiceGuildsLang.set(msg.guildId, lang.lang);
 					db.query( 'SELECT lang FROM discord WHERE guild = $1 AND channel IS NULL', [msg.guildId] ).then( ({rows:[row]}) => {
 						console.log( '- Voice language successfully updated.' );
-						voice[msg.guildId] = row.lang;
+						voiceGuildsLang.set(msg.guildId, row.lang);
 					}, dberror => {
 						console.log( '- Error while getting the voice language: ' + dberror );
 					} );
 					msg.replyMsg( lang.get('voice.enabled') + '\n`' + lang.get('voice.channel') + ' – <' + lang.get('voice.name') + '>`', true );
 				}
 				else {
-					delete voice[msg.guildId];
+					voiceGuildsLang.delete(msg.guildId);
 					msg.replyMsg( lang.get('voice.disabled'), true );
 				}
 			}, dberror => {
@@ -43,10 +43,10 @@ function cmd_voice(lang, msg, args, line, wiki) {
 			} );
 		}
 	}
-	if ( !msg.channel.isGuild() || !pause[msg.guildId] ) this.LINK(lang, msg, line, wiki);
+	if ( !msg.inGuild() || !pausedGuilds.has(msg.guildId) ) this.LINK(lang, msg, line, wiki);
 }
 
-module.exports = {
+export default {
 	name: 'voice',
 	everyone: true,
 	pause: true,

+ 16 - 19
cmds/wiki/diff.js

@@ -1,21 +1,23 @@
-const {MessageEmbed} = require('discord.js');
-const logging = require('../../util/logging.js');
+import { MessageEmbed } from 'discord.js';
+import logging from '../../util/logging.js';
+import { got, htmlToPlain, htmlToDiscord, escapeFormatting } from '../../util/functions.js';
+import diffParser from '../../util/edit_diff.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {timeoptions} = require('../../util/default.json');
-const {got, htmlToPlain, htmlToDiscord, escapeFormatting} = require('../../util/functions.js');
-const diffParser = require('../../util/edit_diff.js');
 
 /**
  * Processes a Gamepedia edit.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the edit.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the edit.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  * @param {MessageEmbed} [embed] - The embed for the page.
  */
-function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, noEmbed, embed) {
+export default function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, noEmbed, embed) {
 	if ( args[0] ) {
 		var error = false;
 		var title = '';
@@ -56,7 +58,7 @@ function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, noEmbed, embed
 		else {
 			got.get( wiki + 'api.php?action=compare&prop=ids|diff' + ( title ? '&fromtitle=' + encodeURIComponent( title ) : '&fromrev=' + revision ) + '&torelative=' + relative + '&format=json' ).then( response => {
 				var body = response.body;
-				if ( body && body.warnings ) log_warn(body.warnings);
+				if ( body && body.warnings ) log_warning(body.warnings);
 				if ( response.statusCode !== 200 || !body || !body.compare ) {
 					var noerror = false;
 					if ( body && body.error ) {
@@ -139,10 +141,10 @@ function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, noEmbed, embed
 
 /**
  * Sends a Gamepedia edit.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String[]} args - The command arguments.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the edit.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the edit.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
@@ -151,7 +153,7 @@ function gamepedia_diff(lang, msg, args, wiki, reaction, spoiler, noEmbed, embed
 function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, noEmbed, compare) {
 	got.get( wiki + 'api.php?uselang=' + lang.lang + '&action=query&meta=siteinfo&siprop=general&list=tags&tglimit=500&tgprop=displayname&prop=revisions&rvslots=main&rvprop=ids|timestamp|flags|user|size|parsedcomment|tags' + ( args.length === 1 || args[0] === args[1] ? '|content' : '' ) + '&revids=' + args.join('|') + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
 			if ( wiki.noWiki(response.url, response.statusCode) ) {
 				console.log( '- This wiki doesn\'t exist!' );
@@ -204,13 +206,13 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, noEmbed,
 			var pagelink = wiki.toLink(title, {diff,oldid});
 			var text = '<' + pagelink + '>';
 			if ( msg.showEmbed() && !noEmbed ) {
-				var 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 embed = new MessageEmbed().setAuthor( {name: 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 whitespace = '__' + lang.get('diff.info.whitespace') + '__';
 				if ( !compare && oldid ) got.get( wiki + 'api.php?action=compare&prop=diff&fromrev=' + oldid + '&torev=' + diff + '&format=json' ).then( cpresponse => {
 					var cpbody = cpresponse.body;
-					if ( cpbody && cpbody.warnings ) log_warn(cpbody.warnings);
+					if ( cpbody && cpbody.warnings ) log_warning(cpbody.warnings);
 					if ( cpresponse.statusCode !== 200 || !cpbody || !cpbody.compare || cpbody.compare['*'] === undefined ) {
 						var noerror = false;
 						if ( cpbody && cpbody.error ) {
@@ -299,9 +301,4 @@ function gamepedia_diff_send(lang, msg, args, wiki, reaction, spoiler, noEmbed,
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = {
-	name: 'diff',
-	run: gamepedia_diff
-};
+}

+ 17 - 17
functions/discussion.js → cmds/wiki/discussion.js

@@ -1,21 +1,23 @@
-const htmlparser = require('htmlparser2');
-const {MessageEmbed, Util} = require('discord.js');
-const {limit: {discussion: discussionLimit}} = require('../util/default.json');
-const {got, htmlToDiscord, escapeFormatting} = require('../util/functions.js');
+import htmlparser from 'htmlparser2';
+import { MessageEmbed, Util } from 'discord.js';
+import { got, htmlToDiscord, escapeFormatting } from '../../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const {limit: {discussion: discussionLimit}} = require('../../util/default.json');
 
 /**
  * Processes discussion commands.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the page.
  * @param {String} title - The title of the discussion post.
  * @param {String} sitename - The sitename of the wiki.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  */
-function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler, noEmbed) {
-	var limit = discussionLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+export default function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler, noEmbed) {
+	var limit = discussionLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
 	if ( !title ) {
 		var pagelink = wiki + 'f';
 		if ( !msg.showEmbed() || noEmbed ) {
@@ -24,7 +26,7 @@ function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler,
 			if ( reaction ) reaction.removeEmoji();
 			return;
 		}
-		var embed = new MessageEmbed().setAuthor( sitename ).setTitle( lang.get('discussion.main') ).setURL( pagelink );
+		var embed = new MessageEmbed().setAuthor( {name: sitename} ).setTitle( lang.get('discussion.main') ).setURL( pagelink );
 		got.get( wiki + 'f', {
 			responseType: 'text'
 		} ).then( descresponse => {
@@ -73,7 +75,7 @@ function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler,
 			}
 			else if ( body._embedded['doc:posts'].length ) {
 				var posts = body._embedded['doc:posts'];
-				var embed = new MessageEmbed().setAuthor( sitename );
+				var embed = new MessageEmbed().setAuthor( {name: sitename} );
 				
 				if ( posts.some( post => post.id === title ) ) {
 					discussion_send(lang, msg, wiki, posts.find( post => post.id === title ), embed, spoiler, noEmbed);
@@ -170,7 +172,7 @@ function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler,
 			}
 			else if ( body._embedded.threads.length ) {
 				var threads = body._embedded.threads;
-				var embed = new MessageEmbed().setAuthor( sitename );
+				var embed = new MessageEmbed().setAuthor( {name: sitename} );
 				
 				if ( threads.some( thread => thread.id === title ) ) {
 					discussion_send(lang, msg, wiki, threads.find( thread => thread.id === title ), embed, spoiler, noEmbed);
@@ -251,9 +253,9 @@ function fandom_discussion(lang, msg, wiki, title, sitename, reaction, spoiler,
 
 /**
  * Send discussion posts.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the page.
  * @param {Object} discussion - The discussion post.
  * @param {import('discord.js').MessageEmbed} embed - The embed for the page.
  * @param {String} spoiler - If the response is in a spoiler.
@@ -269,7 +271,7 @@ function discussion_send(lang, msg, wiki, discussion, embed, spoiler, noEmbed) {
 		var pagelink = wiki + 'f/p/' + discussion.threadId + '/r/' + discussion.id;
 	}
 	if ( !msg.showEmbed() || noEmbed ) msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler );
-	embed.setURL( pagelink ).setFooter( discussion.createdBy.name, discussion.createdBy.avatarUrl ).setTimestamp( discussion.creationDate.epochSecond * 1000 );
+	embed.setURL( pagelink ).setFooter( {text: discussion.createdBy.name, iconURL: discussion.createdBy.avatarUrl} ).setTimestamp( discussion.creationDate.epochSecond * 1000 );
 	var description = '';
 	switch ( discussion.funnel ) {
 		case 'IMAGE':
@@ -394,6 +396,4 @@ function discussion_formatting(jsonModel) {
 			break;
 	}
 	return description;
-}
-
-module.exports = fandom_discussion;
+}

+ 7 - 0
cmds/wiki/functions.js

@@ -0,0 +1,7 @@
+export {default as diff} from './diff.js';
+export {default as discussion} from './discussion.js';
+export {default as overview} from './overview.js';
+export {default as random} from './random.js';
+export {default as search} from './search.js';
+export {default as special_page} from './special_page.js';
+export {default as user} from './user.js';

+ 39 - 42
cmds/wiki/general.js

@@ -1,37 +1,30 @@
-const {MessageEmbed} = require('discord.js');
-const parse_page = require('../../functions/parse_page.js');
-const phabricator = require('../../functions/phabricator.js');
-const logging = require('../../util/logging.js');
-const {got, htmlToDiscord, escapeFormatting, partialURIdecode} = require('../../util/functions.js');
-const extract_desc = require('../../util/extract_desc.js');
+import { readdir } from 'fs';
+import { MessageEmbed } from 'discord.js';
+import parse_page from '../../functions/parse_page.js';
+import phabricator from '../../functions/phabricator.js';
+import logging from '../../util/logging.js';
+import { got, htmlToDiscord, escapeFormatting, partialURIdecode, breakOnTimeoutPause } from '../../util/functions.js';
+import extract_desc from '../../util/extract_desc.js';
+import Wiki from '../../util/wiki.js';
+import * as fn from './functions.js'
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {interwiki: interwikiLimit}, wikiProjects} = require('../../util/default.json');
-const Wiki = require('../../util/wiki.js');
 const {wikis: mcw} = require('../minecraft/commands.json');
 
-const fs = require('fs');
-var fn = {
-	special_page: require('../../functions/special_page.js'),
-	discussion: require('../../functions/discussion.js')
-};
-fs.readdir( './cmds/wiki', (error, files) => {
-	if ( error ) return error;
-	files.filter( file => ( file !== 'general.js' && file.endsWith('.js') ) ).forEach( file => {
-		var command = require('./' + file);
-		fn[command.name] = command.run;
-	} );
-} );
 var minecraft = {};
-fs.readdir( './cmds/minecraft', (error, files) => {
+readdir( './cmds/minecraft', (error, files) => {
 	if ( error ) return error;
 	files.filter( file => file.endsWith('.js') ).forEach( file => {
-		var command = require('../minecraft/' + file);
-		minecraft[command.name] = command.run;
+		import('../minecraft/' + file).then( ({default: command}) => {
+			minecraft[command.name] = command.run;
+		} );
 	} );
 } );
 
 /**
  * Checks a Gamepedia wiki.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} title - The page title.
  * @param {Wiki} wiki - The wiki for the page.
@@ -44,7 +37,7 @@ fs.readdir( './cmds/minecraft', (error, files) => {
  * @param {String} [interwiki] - The fallback interwiki link.
  * @param {Number} [selfcall] - The amount of followed interwiki links.
  */
-function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', noEmbed = false, querystring = new URLSearchParams(), fragment = '', interwiki = '', selfcall = 0) {
+export default function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '', noEmbed = false, querystring = new URLSearchParams(), fragment = '', interwiki = '', selfcall = 0) {
 	var full_title = title;
 	if ( title.includes( '#' ) ) {
 		fragment = title.split('#').slice(1).join('#').trim().replace( /(?:%[\dA-F]{2})+/g, partialURIdecode );
@@ -91,7 +84,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 	}
 	got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|specialpagealiases&iwurl=true' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&converttitles=true&titles=%1F' + encodeURIComponent( ( aliasInvoke === 'search' ? full_title.split(' ').slice(1).join(' ') : title ).replace( /\x1F/g, '\ufffd' ) ) + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query ) {
 			if ( interwiki ) msg.sendChannel( spoiler + ( noEmbed ? '<' : ' ' ) + interwiki + ( noEmbed ? '>' : ' ' ) + spoiler );
 			else if ( wiki.noWiki(response.url, response.statusCode) ) {
@@ -187,7 +180,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				var iw_parts = querypage.title.split(':');
 				var iw = new Wiki('https://' + iw_parts[1] + '.miraheze.org/w/');
 				var iw_link = iw.toLink(iw_parts.slice(2).join(':'), querystring, fragment);
-				var maxselfcall = interwikiLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+				var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
 				if ( selfcall < maxselfcall ) {
 					selfcall++;
 					return this.general(lang, msg, iw_parts.slice(2).join(':'), iw, '!!' + iw.hostname + ' ', reaction, spoiler, noEmbed, querystring, fragment, iw_link, selfcall);
@@ -201,7 +194,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 			if ( ( querypage.missing !== undefined && querypage.known === undefined && !( noRedirect || querypage.categoryinfo ) ) || querypage.invalid !== undefined ) return got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&generator=search&gsrnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json' ).then( srresponse => {
 				logging(wiki, msg.guildId, 'general', 'search');
 				var srbody = srresponse.body;
-				if ( srbody?.warnings ) log_warn(srbody.warnings);
+				if ( srbody?.warnings ) log_warning(srbody.warnings);
 				if ( srresponse.statusCode !== 200 || !srbody || srbody.batchcomplete === undefined ) {
 					console.log( '- ' + srresponse.statusCode + ': Error while getting the search results: ' + srbody?.error?.info );
 					msg.sendChannelError( spoiler + '<' + wiki.toLink('Special:Search', {search:title}) + '>' + spoiler );
@@ -210,12 +203,19 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 					return;
 				}
 				if ( querypage.ns === 12 && wiki.isFandom() ) {
-					return got.head( wiki.articleURL.href.replace( '$1', encodeURIComponent( querypage.title ) ), {
+					return got.head( wiki.articleURL.href.replace( '$1', encodeURIComponent( querypage.title ).replace( /%3A/g, ':' ) ), {
 						followRedirect: false
 					} ).then( hresponse => {
 						if ( hresponse.statusCode === 301 && /^https:\/\/[a-z\d-]{1,50}\.fandom\.com\/(?:(?!wiki\/)[a-z-]{2,12}\/)?wiki\/Help:/.test( hresponse.headers?.location ) ) {
 							var location = hresponse.headers.location.split('wiki/');
-							var maxselfcall = interwikiLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+							if ( location[0] === wiki.href && location.slice(1).join('wiki/').replace( /(?:%[\dA-F]{2})+/g, partialURIdecode ).replace( /_/g, ' ' ) === querypage.title ) {
+								if ( srbody.query ) return srbody;
+								msg.reactEmoji('🤷');
+								
+								if ( reaction ) reaction.removeEmoji();
+								return;
+							}
+							var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
 							if ( selfcall < maxselfcall ) {
 								selfcall++;
 								return this.general(lang, msg, location.slice(1).join('wiki/'), new Wiki(location[0]), cmd, reaction, spoiler, noEmbed, querystring, fragment, '', selfcall);
@@ -241,7 +241,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				if ( !srbody.query ) {
 					return got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&generator=search&gsrwhat=text&gsrnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(body.query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&gsrlimit=1&gsrsearch=' + encodeURIComponent( title ) + '&format=json' ).then( tsrresponse => {
 						var tsrbody = tsrresponse.body;
-						if ( tsrbody?.warnings ) log_warn(tsrbody.warnings);
+						if ( tsrbody?.warnings ) log_warning(tsrbody.warnings);
 						if ( tsrresponse.statusCode !== 200 || !tsrbody || tsrbody.batchcomplete === undefined ) {
 							if ( tsrbody?.error?.code !== 'search-text-disabled' ) console.log( '- ' + tsrresponse.statusCode + ': Error while getting the text search results: ' + tsrbody?.error?.info );
 						}
@@ -263,7 +263,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				querypage.uselang = uselang;
 				var pagelink = wiki.toLink(querypage.title, querystring, fragment);
 				var text = '';
-				var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
+				var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
 				if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
 					var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
 					if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
@@ -299,7 +299,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				}
 				else embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
 				
-				var prefix = ( msg.channel.isGuild() && patreons[msg.guildId] || process.env.prefix );
+				var prefix = ( patreonGuildsPrefix.get(msg.guildId) ?? process.env.prefix );
 				var linksuffix = ( querystring.toString() ? '?' + querystring : '' ) + ( fragment ? '#' + fragment : '' );
 				if ( title.replace( /[_-]/g, ' ' ).toLowerCase() === querypage.title.replace( /-/g, ' ' ).toLowerCase() ) {
 					text = '';
@@ -350,7 +350,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				var pagelink = wiki.toLink(body.query.namespaces['-1']['*'] + ':' + ( filepath?.aliases?.[0] || 'FilePath' ) + querypage.title.replace( body.query.namespaces['-2']['*'] + ':', '/' ), querystring, fragment);
 				var embed = null;
 				if ( !noEmbed ) {
-					embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setDescription( '[' + lang.get('search.media') + '](' + wiki.toLink(querypage.title, '', '', true) + ')' );
+					embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setDescription( '[' + lang.get('search.media') + '](' + wiki.toLink(querypage.title, '', '', true) + ')' );
 					if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.title.toLowerCase()) ) embed.setImage( pagelink );
 				}
 				
@@ -362,7 +362,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 			logging(wiki, msg.guildId, 'general');
 			var pagelink = wiki.toLink(querypage.title, querystring, ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ));
 			var text = '';
-			var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
+			var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
 			if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
 				var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
 				if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
@@ -415,9 +415,8 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 			return parse_page(lang, msg, spoiler + '<' + pagelink + '>' + text + spoiler, ( noEmbed ? null : embed ), wiki, reaction, querypage, ( querypage.title === body.query.general.mainpage ? '' : new URL(body.query.general.logo, wiki).href ), ( fragment || ( body.query.redirects && body.query.redirects[0].tofragment ) || '' ), pagelink);
 		}
 		if ( body.query.interwiki ) {
-			if ( msg.channel.isGuild() && pause[msg.guildId] ) {
+			if ( breakOnTimeoutPause(msg) ) {
 				if ( reaction ) reaction.removeEmoji();
-				console.log( '- Aborted, paused.' );
 				return;
 			}
 			var iw = new URL(body.query.interwiki[0].url.replace( /\\/g, '%5C' ).replace( /@(here|everyone)/g, '%40$1' ), wiki);
@@ -430,7 +429,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				return phabricator(lang, msg, wiki, iw, reaction, spoiler, noEmbed);
 			}
 			logging(wiki, msg.guildId, 'interwiki');
-			var maxselfcall = interwikiLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+			var maxselfcall = interwikiLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
 			if ( selfcall < maxselfcall && ['http:','https:'].includes( iw.protocol ) ) {
 				selfcall++;
 				if ( iw.hostname.endsWith( '.gamepedia.com' ) ) {
@@ -470,10 +469,10 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 			uselang, noRedirect
 		};
 		var pagelink = wiki.toLink(querypage.title, querystring, fragment);
-		var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setThumbnail( new URL(body.query.general.logo, wiki).href );
+		var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink ).setThumbnail( new URL(body.query.general.logo, wiki).href );
 		got.get( wiki + 'api.php?uselang=' + uselang + '&action=query' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=info|pageprops|extracts&ppprop=description|displaytitle|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&titles=' + encodeURIComponent( querypage.title ) + '&format=json' ).then( mpresponse => {
 			var mpbody = mpresponse.body;
-			if ( mpbody && mpbody.warnings ) log_warn(body.warnings);
+			if ( mpbody && mpbody.warnings ) log_warning(body.warnings);
 			if ( mpresponse.statusCode !== 200 || !mpbody || mpbody.batchcomplete === undefined || !mpbody.query ) {
 				console.log( '- ' + mpresponse.statusCode + ': Error while getting the main page: ' + ( mpbody && mpbody.error && mpbody.error.info ) );
 				return;
@@ -527,6 +526,4 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = gamepedia_check_wiki;
+}

+ 16 - 17
cmds/wiki/overview.js

@@ -1,24 +1,26 @@
-const {MessageEmbed} = require('discord.js');
-const logging = require('../../util/logging.js');
+import { MessageEmbed } from 'discord.js';
+import logging from '../../util/logging.js';
+import { got, toFormatting, toPlaintext, escapeFormatting } from '../../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {timeoptions} = require('../../util/default.json');
-const {got, toFormatting, toPlaintext, escapeFormatting} = require('../../util/functions.js');
 
 /**
  * Sends a Gamepedia wiki overview.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the overview.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the overview.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  * @param {URLSearchParams} [querystring] - The querystring for the link.
  * @param {String} [fragment] - The section for the link.
  */
-function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, querystring = new URLSearchParams(), fragment = '') {
+export default function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, querystring = new URLSearchParams(), fragment = '') {
 	var uselang = ( querystring.getAll('variant').pop() || querystring.getAll('uselang').pop() || lang.lang );
 	got.get( wiki + 'api.php?uselang=' + uselang + '&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=' + uselang + '&list=logevents&ledir=newer&lelimit=1&leprop=timestamp&titles=Special:Statistics&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.pages ) {
 			if ( wiki.noWiki(response.url, response.statusCode) ) {
 				console.log( '- This wiki doesn\'t exist!' );
@@ -94,7 +96,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, queryst
 		var text = '<' + pagelink + '>';
 		var embed = null;
 		if ( msg.showEmbed() && !noEmbed ) {
-			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( {name: 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';
@@ -155,7 +157,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, queryst
 				return Promise.all([
 					( founder[1] > 0 ? got.get( wiki + 'api.php?action=query&list=users&usprop=&ususerids=' + founder[1] + '&format=json' ).then( usresponse => {
 						var usbody = usresponse.body;
-						if ( usbody && usbody.warnings ) log_warn(usbody.warnings);
+						if ( usbody && usbody.warnings ) log_warning(usbody.warnings);
 						if ( usresponse.statusCode !== 200 || !usbody || !usbody.query || !usbody.query.users || !usbody.query.users[0] ) {
 							console.log( '- ' + usresponse.statusCode + ': Error while getting the wiki founder: ' + ( usbody && usbody.error && usbody.error.info ) );
 							founder[1] = 'ID: ' + founder[1];
@@ -208,7 +210,9 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, queryst
 					if ( manager[1] ) embed.addField( manager[0], '[' + manager[1] + '](' + wiki.toLink('User:' + manager[1], '', '', true) + ') ([' + lang.get('overview.talk') + '](' + wiki.toLink('User talk:' + manager[1], '', '', true) + '))', true );
 					if ( founder[1] ) embed.addField( founder[0], founder[1], true );
 					if ( crossover[1] ) embed.addField( crossover[0], crossover[1], true );
-					embed.addField( license[0], license[1], true ).addField( misermode[0], misermode[1], true ).setFooter( lang.get('overview.inaccurate') + ( wikiid ? ' • ' + lang.get('overview.wikiid') + ' ' + wikiid : '' ) );
+					embed.addField( license[0], license[1], true ).addField( misermode[0], misermode[1], true ).setFooter( {
+						text: lang.get('overview.inaccurate') + ( wikiid ? ' • ' + lang.get('overview.wikiid') + ' ' + wikiid : '' )
+					} );
 					if ( description[1] ) embed.addField( description[0], description[1] );
 					if ( image[1] ) embed.addField( image[0], image[1] ).setImage( image[1] );
 					if ( readonly[1] ) embed.addField( readonly[0], readonly[1] );
@@ -242,7 +246,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, queryst
 		if ( msg.showEmbed() && !noEmbed ) {
 			embed.addField( version[0], version[1], true ).addField( language[0], language[1], true );
 			if ( rtl[1] ) embed.addField( rtl[0], rtl[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 ).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( {text: lang.get('overview.inaccurate')} );
 			if ( readonly[1] ) embed.addField( readonly[0], readonly[1] );
 		}
 		else {
@@ -268,9 +272,4 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler, noEmbed, queryst
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = {
-	name: 'overview',
-	run: gamepedia_overview
-};
+}

+ 12 - 17
cmds/wiki/random.js

@@ -1,14 +1,14 @@
-const {MessageEmbed} = require('discord.js');
-const parse_page = require('../../functions/parse_page.js');
-const logging = require('../../util/logging.js');
-const {got, toMarkdown, htmlToDiscord, escapeFormatting} = require('../../util/functions.js');
-const extract_desc = require('../../util/extract_desc.js');
+import { MessageEmbed } from 'discord.js';
+import parse_page from '../../functions/parse_page.js';
+import logging from '../../util/logging.js';
+import { got, toMarkdown, htmlToDiscord, escapeFormatting } from '../../util/functions.js';
+import extract_desc from '../../util/extract_desc.js';
 
 /**
  * Sends a random Gamepedia page.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the page.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
@@ -16,11 +16,11 @@ const extract_desc = require('../../util/extract_desc.js');
  * @param {URLSearchParams} [querystring] - The querystring for the link.
  * @param {String} [fragment] - The section for the link.
  */
-function gamepedia_random(lang, msg, wiki, reaction, spoiler, noEmbed, namespace = ['0', '*'], querystring = new URLSearchParams(), fragment = '') {
+export default function gamepedia_random(lang, msg, wiki, reaction, spoiler, noEmbed, namespace = ['0', '*'], querystring = new URLSearchParams(), fragment = '') {
 	var uselang = ( querystring.getAll('variant').pop() || querystring.getAll('uselang').pop() || lang.lang );
 	got.get( wiki + 'api.php?uselang=' + uselang + '&action=query&meta=allmessages|siteinfo&amenableparser=true&amtitle=Special:Random&ammessages=randompage|randompage-nopages&amargs=%1F' + namespace[1] + '%1F' + namespace[0].split('|').length + '&siprop=general&prop=categoryinfo|info|pageprops|pageimages|extracts&piprop=original|name&ppprop=description|displaytitle|page_image_free|disambiguation|infoboxes&explaintext=true&exsectionformat=raw&exlimit=1&converttitles=true&generator=random&grnfilterredir=nonredirects&grnlimit=1&grnnamespace=' + namespace[0] + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.general ) {
 			if ( wiki.noWiki(response.url, response.statusCode) ) {
 				console.log( '- This wiki doesn\'t exist!' );
@@ -41,7 +41,7 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler, noEmbed, namespace
 			var pagelink = wiki.toLink(title, querystring, fragment);
 			var embed = null;
 			if ( msg.showEmbed() && !noEmbed ) {
-				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( {name: 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';
@@ -61,7 +61,7 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler, noEmbed, namespace
 		var querypage = Object.values(body.query.pages)[0];
 		var pagelink = wiki.toLink(querypage.title, querystring, fragment);
 		var text = '';
-		var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
+		var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
 		if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
 			var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
 			if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
@@ -123,9 +123,4 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler, noEmbed, namespace
 		}
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = {
-	name: 'random',
-	run: gamepedia_random
-};
+}

+ 14 - 17
cmds/wiki/search.js

@@ -1,19 +1,21 @@
-const {MessageEmbed, Util} = require('discord.js');
-const {got, escapeFormatting} = require('../../util/functions.js');
+import { MessageEmbed, Util } from 'discord.js';
+import { got, escapeFormatting } from '../../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {search: searchLimit}} = require('../../util/default.json');
 
 /**
  * Searches a Gamepedia wiki.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} searchterm - The searchterm.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the search.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the search.
  * @param {Object} query - The siteinfo from the wiki.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  */
-function gamepedia_search(lang, msg, searchterm, wiki, query, reaction, spoiler, noEmbed) {
+export default function gamepedia_search(lang, msg, searchterm, wiki, query, reaction, spoiler, noEmbed) {
 	if ( searchterm.length > 250 ) {
 		searchterm = searchterm.substring(0, 250);
 		msg.reactEmoji('⚠️');
@@ -22,20 +24,20 @@ function gamepedia_search(lang, msg, searchterm, wiki, query, reaction, spoiler,
 	var pagelink = wiki.toLink('Special:Search', {search:searchterm,fulltext:1});
 	var resultText = '<' + pagelink + '>';
 	var embed = null;
-	if ( msg.showEmbed() && !noEmbed ) embed = new MessageEmbed().setAuthor( query.general.sitename ).setTitle( '`' + searchterm + '`' ).setURL( pagelink );
+	if ( msg.showEmbed() && !noEmbed ) embed = new MessageEmbed().setAuthor( {name: query.general.sitename} ).setTitle( '`' + searchterm + '`' ).setURL( pagelink );
 	else resultText += '\n\n**`' + searchterm + '`**';
 	var querypage = ( Object.values(( query.pages || {} ))?.[0] || {title:'',ns:0,invalid:''} );
-	var limit = searchLimit[( patreons[msg.guildId] ? 'patreon' : 'default' )];
+	var limit = searchLimit[( patreonGuildsPrefix.has(msg.guildId) ? 'patreon' : 'default' )];
 	got.get( wiki + 'api.php?action=query&titles=Special:Search&list=search&srinfo=totalhits&srprop=redirecttitle|sectiontitle&srnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&srlimit=' + limit + '&srsearch=' + encodeURIComponent( searchterm ) + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body?.warnings ) log_warn(body.warnings);
+		if ( body?.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body?.query?.search || body.batchcomplete === undefined ) {
 			return console.log( '- ' + response.statusCode + ': Error while getting the search results: ' + body?.error?.info );
 		}
 		if ( body.query.search.length < limit ) {
 			return got.get( wiki + 'api.php?action=query&list=search&srwhat=text&srinfo=totalhits&srprop=redirecttitle|sectiontitle&srnamespace=4|12|14|' + ( querypage.ns >= 0 ? querypage.ns + '|' : '' ) + Object.values(query.namespaces).filter( ns => ns.content !== undefined ).map( ns => ns.id ).join('|') + '&srlimit=' + limit + '&srsearch=' + encodeURIComponent( searchterm ) + '&format=json' ).then( tresponse => {
 				var tbody = tresponse.body;
-				if ( tbody?.warnings ) log_warn(tbody.warnings);
+				if ( tbody?.warnings ) log_warning(tbody.warnings);
 				if ( tresponse.statusCode !== 200 || !tbody?.query?.search || tbody.batchcomplete === undefined ) {
 					return console.log( '- ' + tresponse.statusCode + ': Error while getting the text search results: ' + tbody?.error?.info );
 				}
@@ -134,8 +136,8 @@ function gamepedia_search(lang, msg, searchterm, wiki, query, reaction, spoiler,
 			footer = lang.get('search.results', body.query.searchinfo.totalhits.toLocaleString(lang.get('dateformat')), body.query.searchinfo.totalhits);
 		}
 		if ( msg.showEmbed() && !noEmbed ) {
-			embed.setDescription( Util.splitMessage( description.join('\n') )[0] );
-			if ( footer ) embed.setFooter( footer );
+			if ( description.length ) embed.setDescription( Util.splitMessage( description.join('\n') )[0] );
+			if ( footer ) embed.setFooter( {text: footer} );
 		}
 		else {
 			if ( description.length ) resultText += '\n' + Util.splitMessage( description.join('\n'), {maxLength: 1995 - resultText.length - footer.length} )[0];
@@ -148,9 +150,4 @@ function gamepedia_search(lang, msg, searchterm, wiki, query, reaction, spoiler,
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = {
-	name: 'search',
-	run: gamepedia_search
-};
+}

+ 16 - 16
functions/special_page.js → cmds/wiki/special_page.js

@@ -1,7 +1,9 @@
-const {MessageEmbed, Util} = require('discord.js');
-const logging = require('../util/logging.js');
-const {timeoptions} = require('../util/default.json');
-const {got, toMarkdown, escapeFormatting} = require('../util/functions.js');
+import { MessageEmbed, Util } from 'discord.js';
+import logging from '../../util/logging.js';
+import { got, toMarkdown, escapeFormatting } from '../../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const {timeoptions} = require('../../util/default.json');
 
 const overwrites = {
 	randompage: (fn, lang, msg, wiki, querystring, fragment, reaction, spoiler, noEmbed, args, embed, query) => {
@@ -145,23 +147,23 @@ const descriptions = {
 
 /**
  * Processes special pages.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {Object} querypage - The details of the special page.
  * @param {String} querypage.title - The title of the special page.
  * @param {String} querypage.uselang - The language of the special page.
  * @param {String} specialpage - The canonical name of the special page.
  * @param {Object} query - The siteinfo from the wiki.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the page.
  * @param {URLSearchParams} querystring - The querystring for the link.
  * @param {String} fragment - The section for the link.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  */
-function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, query, wiki, querystring, fragment, reaction, spoiler, noEmbed) {
+export default function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, query, wiki, querystring, fragment, reaction, spoiler, noEmbed) {
 	var pagelink = wiki.toLink(title, querystring, fragment);
-	var embed = new MessageEmbed().setAuthor( query.general.sitename ).setTitle( escapeFormatting(title) ).setURL( pagelink ).setThumbnail( new URL(query.general.logo, wiki).href );
+	var embed = new MessageEmbed().setAuthor( {name: query.general.sitename} ).setTitle( escapeFormatting(title) ).setURL( pagelink ).setThumbnail( new URL(query.general.logo, wiki).href );
 	if ( overwrites.hasOwnProperty(specialpage) ) {
 		var args = title.split('/').slice(1,3);
 		overwrites[specialpage](this, lang, msg, wiki, querystring, fragment, reaction, spoiler, noEmbed, args, embed, query);
@@ -175,11 +177,11 @@ function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, quer
 		return;
 	}
 	if ( specialpage === 'recentchanges' && msg.isAdmin() ) {
-		embed.addField( lang.get('rcscript.title'), lang.get('rcscript.ad', ( patreons[msg.guildId] || process.env.prefix ), '[RcGcDw](https://gitlab.com/piotrex43/RcGcDw)') );
+		embed.addField( lang.get('rcscript.title'), lang.get('rcscript.ad', ( patreonGuildsPrefix.get(msg.guildId) ?? 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=' + encodeURIComponent( specialpage ) + '|' + ( descriptions.hasOwnProperty(specialpage) ? descriptions[specialpage] : encodeURIComponent( specialpage ) + '-summary' ) + ( querypages.hasOwnProperty(specialpage) ? querypages[specialpage][0] : '' ) + '&converttitles=true&titles=%1F' + encodeURIComponent( title ) + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || body?.batchcomplete === undefined ) {
 			console.log( '- ' + response.statusCode + ': Error while getting the special page: ' + ( body && body.error && body.error.info ) );
 			return;
@@ -199,11 +201,11 @@ function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, quer
 			if ( description.length > 1000 ) description = description.substring(0, 1000) + '\u2026';
 			embed.setDescription( description );
 		}
-		if ( msg.channel.isGuild() && patreons[msg.guildId] && querypages.hasOwnProperty(specialpage) ) {
+		if ( msg.inGuild() && patreonGuildsPrefix.has(msg.guildId) && 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));
+			if ( body.query.querypage?.cached !== undefined ) {
+				embed.setFooter( {text: lang.get('search.cached')} ).setTimestamp(new Date(body.query.querypage.cachedtimestamp));
 			}
 		}
 	}, error => {
@@ -213,6 +215,4 @@ function special_page(lang, msg, {title, uselang = lang.lang}, specialpage, quer
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = special_page;
+}

+ 26 - 30
cmds/wiki/user.js

@@ -1,19 +1,21 @@
-const {MessageEmbed} = require('discord.js');
-const datetimeDifference = require('datetime-difference');
-const global_block = require('../../functions/global_block.js');
-const parse_page = require('../../functions/parse_page.js');
-const logging = require('../../util/logging.js');
-const extract_desc = require('../../util/extract_desc.js');
+import { MessageEmbed } from 'discord.js';
+import datetimeDifference from 'datetime-difference';
+import global_block from '../../functions/global_block.js';
+import parse_page from '../../functions/parse_page.js';
+import logging from '../../util/logging.js';
+import extract_desc from '../../util/extract_desc.js';
+import { got, toMarkdown, toPlaintext, htmlToDiscord, escapeFormatting } from '../../util/functions.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {timeoptions, usergroups} = require('../../util/default.json');
-const {got, toMarkdown, toPlaintext, htmlToDiscord, escapeFormatting} = require('../../util/functions.js');
 
 /**
  * Processes a Gamepedia user.
- * @param {import('../../util/i18n.js')} lang - The user language.
+ * @param {import('../../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} namespace - The user namespace on the wiki.
  * @param {String} username - The name of the user.
- * @param {import('../../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../../util/wiki.js').default} wiki - The wiki for the page.
  * @param {URLSearchParams} querystring - The querystring for the link.
  * @param {String} fragment - The section for the link.
  * @param {Object} querypage - The user page on the wiki.
@@ -22,11 +24,11 @@ const {got, toMarkdown, toPlaintext, htmlToDiscord, escapeFormatting} = require(
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {Boolean} noEmbed - If the response should be without an embed.
  */
-function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragment, querypage, contribs, reaction, spoiler, noEmbed) {
+export default function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragment, querypage, contribs, reaction, spoiler, noEmbed) {
 	if ( /^(?:(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{2})?|(?:[\dA-F]{1,4}:){7}[\dA-F]{1,4}(?:\/\d{2,3})?)$/.test(username) ) return got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=blocks&bkprop=user|by|timestamp|expiry|reason&bkip=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
 		logging(wiki, msg.guildId, 'user', 'ip');
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.blocks || fragment ) {
 			if ( body && body.error && ( body.error.code === 'param_ip' || body.error.code === 'cidrtoobroad' ) || fragment ) {
 				if ( querypage.missing !== undefined || querypage.ns === -1 ) msg.reactEmoji('error');
@@ -35,8 +37,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 					var embed = new MessageEmbed().setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
 					if ( body?.query?.general ) {
 						wiki.updateWiki(body.query.general);
-						embed.setAuthor( body.query.general.sitename );
-						embed.setThumbnail( new URL(body.query.general.logo, wiki).href );
+						embed.setAuthor( {name: body.query.general.sitename} ).setThumbnail( new URL(body.query.general.logo, wiki).href );
 					}
 					if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
 						var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
@@ -175,7 +176,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 		got.get( wiki.updateWiki(body.query.general) + 'api.php?action=query&list=usercontribs&ucprop=&uclimit=50' + ( username.includes( '/' ) ? '&ucuserprefix=' + encodeURIComponent( rangeprefix ) : '&ucuser=' + encodeURIComponent( username ) ) + '&format=json' ).then( ucresponse => {
 			var ucbody = ucresponse.body;
 			if ( rangeprefix && !username.includes( '/' ) ) username = rangeprefix;
-			if ( ucbody && ucbody.warnings ) log_warn(ucbody.warnings);
+			if ( ucbody && ucbody.warnings ) log_warning(ucbody.warnings);
 			if ( ucresponse.statusCode !== 200 || !ucbody || ucbody.batchcomplete === undefined || !ucbody.query || !ucbody.query.usercontribs ) {
 				if ( ucbody && ucbody.error && ucbody.error.code === 'baduser_ucuser' ) {
 					msg.reactEmoji('error');
@@ -193,7 +194,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 			var text = '<' + pagelink + '>';
 			var embed = null;
 			if ( msg.showEmbed() && !noEmbed ) {
-				embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( username ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink(contribs + username, '', '', true) + ')' );
+				embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( username ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink(contribs + username, '', '', true) + ')' );
 				embed.forceTitle = true;
 				if ( querypage.pageprops && querypage.pageprops.description ) {
 					var description = htmlToDiscord( querypage.pageprops.description );
@@ -215,7 +216,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				} );
 			}
 			
-			if ( msg.channel.isGuild() && patreons[msg.guildId] && wiki.isFandom() ) {
+			if ( msg.inGuild() && patreonGuildsPrefix.has(msg.guildId) && wiki.isFandom() ) {
 				if ( msg.showEmbed() && !noEmbed ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.get('user.info.loading') + '**' );
 				else text += '\n\n<a:loading:641343250661113886> **' + lang.get('user.info.loading') + '**';
 
@@ -239,7 +240,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 	logging(wiki, msg.guildId, 'user');
 	got.get( wiki + 'api.php?action=query&meta=siteinfo' + ( wiki.hasCentralAuth() ? '|globaluserinfo&guiprop=groups|editcount|merged&guiuser=' + encodeURIComponent( username ) + '&' : '' ) + '&siprop=general&prop=revisions&rvprop=content|user&rvslots=main&titles=User:' + encodeURIComponent( username ) + '/Discord&list=users&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query || !body.query.users || !body.query.users[0] ) {
 			console.log( '- ' + response.statusCode + ': Error while getting the search results: ' + ( body && body.error && body.error.info ) );
 			msg.sendChannelError( spoiler + '<' + wiki.toLink(namespace + username, querystring, fragment) + '>' + spoiler );
@@ -252,7 +253,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 			if ( querypage.missing !== undefined || querypage.ns === -1 ) msg.reactEmoji('🤷');
 			else {
 				var pagelink = wiki.toLink(querypage.title, querystring, fragment);
-				var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
+				var embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(querypage.title) ).setURL( pagelink );
 				if ( querypage.pageprops && querypage.pageprops.displaytitle ) {
 					var displaytitle = htmlToDiscord( querypage.pageprops.displaytitle );
 					if ( displaytitle.length > 250 ) displaytitle = displaytitle.substring(0, 250) + '\u2026';
@@ -324,7 +325,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 		groupnames.push(...globalgroups);
 		got.get( wiki + 'api.php?action=query&meta=allmessages&amenableparser=true&amincludelocal=true&amargs=' + encodeURIComponent( username ) + '&amlang=' + querypage.uselang + '&ammessages=' + groupnames.map( group => `group-${group}|group-${group}-member` ).join('|') + '&format=json' ).then( gresponse => {
 			var gbody = gresponse.body;
-			if ( gbody && gbody.warnings ) log_warn(gbody.warnings);
+			if ( gbody && gbody.warnings ) log_warning(gbody.warnings);
 			if ( gresponse.statusCode !== 200 || !gbody || gbody.batchcomplete === undefined || !gbody?.query?.allmessages?.length ) {
 				console.log( '- ' + gresponse.statusCode + ': Error while getting the group names: ' + gbody?.error?.info );
 				return;
@@ -438,7 +439,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 			var text = '<' + pagelink + '>';
 			var embed = null;
 			if ( msg.showEmbed() && !noEmbed ) {
-				embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( escapeFormatting(username) ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink(contribs + username, '', '', true) + ')', true );
+				embed = new MessageEmbed().setAuthor( {name: body.query.general.sitename} ).setTitle( escapeFormatting(username) ).setURL( pagelink ).addField( editcount[0], '[' + editcount[1] + '](' + wiki.toLink(contribs + username, '', '', true) + ')', true );
 				embed.forceTitle = true;
 				if ( wiki.hasCentralAuth() ) {
 					embed.addField( lang.get('user.info.globaleditcount'), '[' + body.query.globaluserinfo.editcount.toLocaleString(lang.get('dateformat')) + '](' + wiki.toLink('Special:CentralAuth/' + username, '', '', true) + ')', true ).addField( lang.get('user.info.wikisedited'), '[' + body.query.globaluserinfo.merged.filter( mergedWiki => mergedWiki.editcount ).length.toLocaleString(lang.get('dateformat')) + '](' + wiki.toLink('Special:CentralAuth/' + username, '', '', true) + ')', true );
@@ -517,7 +518,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 						if ( discord.length > 100 ) discord = discord.substring(0, 100) + '\u2026';
 					}
 					if ( discord ) {
-						if ( msg.channel.isGuild() ) {
+						if ( msg.inGuild() ) {
 							var discordmember = msg.guild.members.cache.find( member => {
 								return escapeFormatting(member.user.tag) === discord;
 							} );
@@ -545,7 +546,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 					console.log( '- Error while getting the curse profile: ' + error );
 				} );
 				if ( discord ) {
-					if ( msg.channel.isGuild() ) {
+					if ( msg.inGuild() ) {
 						var discordmember = msg.guild.members.cache.find( member => {
 							return escapeFormatting(member.user.tag) === discord;
 						} );
@@ -564,7 +565,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 					else text += '\n\n**' + block.header + '**\n' + block.text;
 				}
 				
-				if ( msg.channel.isGuild() && patreons[msg.guildId] ) {
+				if ( msg.inGuild() && patreonGuildsPrefix.has(msg.guildId) ) {
 					if ( msg.showEmbed() && !noEmbed ) embed.addField( '\u200b', '<a:loading:641343250661113886> **' + lang.get('user.info.loading') + '**' );
 					else text += '\n\n<a:loading:641343250661113886> **' + lang.get('user.info.loading') + '**';
 					
@@ -577,7 +578,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 				if ( revision?.user === username ) {
 					let discord = ( revision?.slots?.main || revision )['*'].replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/u, '$1#$2' );
 					if ( discord.length > 100 ) discord = discord.substring(0, 100) + '\u2026';
-					if ( msg.channel.isGuild() ) var discordmember = msg.guild.members.cache.find( member => {
+					if ( msg.inGuild() ) var discordmember = msg.guild.members.cache.find( member => {
 						return member.user.tag === discord;
 					} );
 					let discordname = [lang.get('user.info.discord'),escapeFormatting(discord)];
@@ -604,9 +605,4 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 		
 		if ( reaction ) reaction.removeEmoji();
 	} );
-}
-
-module.exports = {
-	name: 'user',
-	run: gamepedia_user
-};
+}

+ 20 - 0
dashboard/functions.js

@@ -0,0 +1,20 @@
+import { get as rcscript_get, post as rcscript_post } from './rcscript.js';
+import { get as settings_get, post as settings_post } from './settings.js';
+import { get as slash_get, post as slash_post } from './slash.js';
+import { get as user_get, post as user_post } from './user.js';
+import { get as verification_get, post as verification_post } from './verification.js';
+
+export const forms = {
+	rcscript: rcscript_get,
+	settings: settings_get,
+	slash: slash_get,
+	user: user_get,
+	verification: verification_get
+};
+export const posts = {
+	rcscript: rcscript_post,
+	settings: settings_post,
+	slash: slash_post,
+	user: user_post,
+	verification: verification_post
+};

+ 11 - 17
dashboard/guilds.js

@@ -1,30 +1,26 @@
-const cheerio = require('cheerio');
+import { readFileSync } from 'fs';
+import cheerio from 'cheerio';
+import { forms } from './functions.js';
+import Lang from './i18n.js';
+import { oauth, enabledOAuth2, settingsData, addWidgets, createNotice } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultPermissions} = require('../util/default.json');
-const Lang = require('./i18n.js');
 const allLangs = Lang.allLangs().names;
-const {oauth, enabledOAuth2, settingsData, addWidgets, createNotice} = require('./util.js');
 
-const forms = {
-	user: require('./user.js').get,
-	settings: require('./settings.js').get,
-	verification: require('./verification.js').get,
-	rcscript: require('./rcscript.js').get,
-	slash: require('./slash.js').get
-};
-
-const file = require('fs').readFileSync('./dashboard/index.html');
+const file = readFileSync('./dashboard/index.html');
 
 /**
  * Let a user view settings
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('./i18n.js')} dashboardLang - The user language.
+ * @param {import('./i18n.js').default} dashboardLang - The user language.
  * @param {String} theme - The display theme
  * @param {import('./util.js').UserSession} userSession - The user session
  * @param {URL} reqURL - The used url
  * @param {String} [action] - The action the user made
  * @param {String[]} [actionArgs] - The arguments for the action
  */
-function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action, actionArgs) {
+export default function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action, actionArgs) {
 	reqURL.pathname = reqURL.pathname.replace( /^(\/(?:user|guild\/\d+(?:\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?)?)?)(?:\/.*)?$/, '$1' );
 	var args = reqURL.pathname.split('/');
 	var settings = settingsData.get(userSession.user_id);
@@ -257,6 +253,4 @@ function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action
 	res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
 	res.write( body );
 	return res.end();
-}
-
-module.exports = dashboard_guilds;
+}

+ 8 - 5
dashboard/i18n.js

@@ -1,13 +1,15 @@
+import { escapeText } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultSettings} = require('../util/default.json');
-const {escapeText} = require('./util.js');
-var i18n = require('./i18n/allLangs.json');
+const i18n = require('./i18n/allLangs.json');
 Object.keys(i18n.allLangs.names).forEach( lang => i18n[lang] = require('./i18n/' + lang + '.json') );
 
 /**
  * A language.
  * @class
  */
-class Lang {
+export default class Lang {
 	/**
 	 * Creates a new language.
 	 * @param {String[]} [langs] - The language code.
@@ -28,7 +30,7 @@ class Lang {
 	 * Get a localized message.
 	 * @param {String} message - Name of the message.
 	 * @param {Boolean} escaped - If the message should be HTML escaped.
-	 * @param {(String|import('cheerio'))[]} args - Arguments for the message.
+	 * @param {(String|import('cheerio').default)[]} args - Arguments for the message.
 	 * @returns {String}
 	 */
 	get(message = '', escaped = false, ...args) {
@@ -120,6 +122,7 @@ function plural(lang, number, args) {
 			else text = getArg(args, 2);
 			break;
 		case 'ru':
+		case 'sr':
 		case 'uk':
 			if ( args.length > 2 ) {
 				if ( number % 10 === 1 && number % 100 !== 11 ) text = getArg(args, 0);
@@ -165,4 +168,4 @@ function getArg(args, index) {
 	return ( args.length > index ? args[index] : args[args.length - 1] );
 }
 
-module.exports = Lang;
+export const allLangs = Lang.allLangs;

+ 5 - 0
dashboard/i18n/allLangs.json

@@ -7,6 +7,7 @@
 			"es": "Español (es)",
 			"fr": "Français (fr)",
 			"hi": "हिन्दी (hi)",
+			"it": "Italiano (it)",
 			"ja": "日本語 (ja)",
 			"ko": "한국어 (ko)",
 			"pl": "Polski (pl)",
@@ -44,6 +45,10 @@
 			"hindi": "hi",
 			"हिन्दी": "hi",
 			"हिन्दी (hi)": "hi",
+			"it": "it",
+			"italian":"it",
+			"italiano": "it",
+			"italiano (it)": "it",
 			"ja": "ja",
 			"japanese": "ja",
 			"日本語": "ja",

+ 26 - 10
dashboard/i18n/bn.json

@@ -1,6 +1,6 @@
 {
     "fallback": [
-        "en",
+        " ",
         " ",
         " ",
         " ",
@@ -8,10 +8,10 @@
     ],
     "general": {
         "botlist": {
-            "text": "ব'ট সূচী তে ভোট করে Wiki-Botকে খুঁজতে অন্যদের সাহায্য করুন:",
-            "title": "ব'ের সূচী"
+            "text": "বট সূচীতে ভোট করে উইকি-বট কে খুঁজতে অন্যদের সাহায্য করুন:",
+            "title": "বট সূচী"
         },
-        "delete": "মুছে ফেলুন",
+        "delete": "মুছুন",
         "invite": "উইকি-বটকে আমন্ত্রণ জানান",
         "language": "ভাষা বদলান",
         "login": "লগইন করুন",
@@ -27,11 +27,15 @@
         "theme-light": "লাইট থিমের ব্যবহীর করুন",
         "title": "উইকি-বট সেটিংস",
         "verification": "যাচাইকরণ",
-        "welcome": "<h2>উইকি-ব'টের ড্যাশবোর্ডে আপনার স্বাগতম।</h2>\n<p>উইকি-ব'ট একটি ডিসকোর্ড ব'ট যেটাকে ডিসকোর্ড সার্ভার আর মেডিয়াউইকি সাইটগুলোকে একসাথে আনার জন্যে বানানো হয়েছে। এ উইকি পৃষ্ঠ লিংক করতে, ব্যাবহারকারিদেরকে ভেরিফাই করতে, উইকিতে নতুন পরিবর্তণের খবর দিতে আরও অনেক কিছুতে সাহায্য করে। <a href=\"https://wiki.wikibot.de/wiki/Wiki-Bot_Wiki\" target=\"_blank\">[আরও তথ্য]</a></p>\n<p>এখানে আপনি সেগুলো সার্ভারের জন্যে বিভিন্ন ব'ট সেটিংস বদলাতে পারেন যেগুলোতে আপনার কাছে কাছে Manage Server অনুমতি আছে। শুরু করার জন্যে আপনাকে নিজের ডিসকোর্ড একাউন্ট কানেক্ট করতে হবে, যা আপনি এই বাটন দিয়ে করতে পারেন:</p>"
+        "welcome": "<h2>উইকি-বট ড্যাশবোর্ডে আপনাকে স্বাগতম।</h2>\n<p>উইকি-বট একটি ডিসকর্ড বট যা ডিসকর্ড সার্ভার এবং মিডিয়াউইকি উইকিগুলোকে একসাথে আনার জন্যে তৈরী করা হয়েছে। এটি উইকি পৃষ্ঠ লিংক করতে, ব্যাবহারকারিদেরকে ভ্যারিফাই করতে, উইকিতে নতুন পরিবর্তনের সংবাদ দিতে এবং আরও অনেক কিছু করতে সাহায্য করে। <a href=\"https://wiki.wikibot.de/wiki/Wiki-Bot_Wiki\" target=\"_blank\">[আরও জানুন]</a></p>\n<p>এখানে আপনার যে সার্ভারগুলোতে Manage Server অনুমতি আছে সেগুলোর বিভিন্ন বট সেটিংস বদলাতে পারেন। শুরু করার জন্যে আপনাকে নিজের ডিসকর্ড এ্যাকাউন্ট সংযুক্ত করতে হবে, যা আপনি এই বাটন দিয়ে করতে পারেন:</p>"
     },
     "indexjs": {
+        "avatar": {
+            "content_type": "সরবরাহিত লিঙ্কটির কনটেন্ট টাইপ $1, কিন্তু শুধুমাত্র নিম্নলিখিতগুলি কনটেন্ট টাইপগুলো অনুমোদিত:",
+            "invalid_url": "URL টি থেকে কোন ছবি ফাইল পাওয়া যায়নি।"
+        },
         "invalid": {
-            "note_http": "এই ওয়েবসাইট HTTPSএর ব্যবহার করেনা!",
+            "note_http": "এই ওয়েবসাইটটির বৈধ TTS/SSL সার্টিফিকেট নেই! নিরাপত্তাজনিত কারণে শুধুমাত্র HTTPS ব্যবহার করা উইকির অনুমোদন আছে।",
             "note_private": "এই উইকিটি ব্যক্তিগত!",
             "note_timeout": "এই লিংকটার উত্তর দিতে অনেক সময় লেগেছে!",
             "text": "URLটি কোনও বৈধ মিডিয়াউইকি সাইটটিতে সমাধান করা যায়নি!",
@@ -115,7 +119,7 @@
             "title": "সেটিংস সংরক্ষিত!"
         },
         "savefail": {
-            "note_http": "এই ওয়েবসাইট HTTPSএর ব্যবহার করেনা!",
+            "note_http": "এই ওয়েবসাইটটির বৈধ TTS/SSL সার্টিফিকেট নেই! নিরাপত্তাজনিত কারণে শুধুমাত্র HTTPS ব্যবহার করা উইকির অনুমোদন আছে।",
             "note_private": "এই উইকিটি ব্যক্তিগত!",
             "note_timeout": "এই লিংকটার উত্তর দিতে অনেক সময় লেগেছে!",
             "text": "সেটিংস সংরক্ষণ করা যায়নি, আবার চেষ্টা করুন।",
@@ -129,6 +133,11 @@
             "text": "কোনো পরিবর্তন আনার আগে অনুগ্রহ করে লগইন করুন।",
             "title": "লগইন করা নাই!"
         },
+        "webhookfail": {
+            "note": "ডিসকর্ড ওয়েবহুক পরিবর্তন করা যায়নি!",
+            "text": "সেটিংস আংশিকভাবে আপডেট করা হয়েছে।",
+            "title": "সেটিংস আংশিকভাবে সেভ হয়েছে!"
+        },
         "wikiblocked": {
             "note": "কারণ:",
             "text": "$1কে রিসেন্ট চেঞ্জেস ওয়েবহুকের রূপে লাগানোর থেকে ব্ল'ক করা হয়েছে।",
@@ -139,6 +148,8 @@
         "desc": "এগুলি $1এর জন্যে রিসেন্ট চেঞ্জেস ওয়েবহুক:",
         "explanation": "<h2>রিসেন্ট চেঞ্জেস ওয়েবহুক</h2>\n<p>উইকি-বট <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>তে আধারিত একটি রিসেন্ট চেঞ্জেস ওয়েবহুক চালাতে পারে। রিসেন্ট চেঞ্জেসকে ইনলাইন লিংক দি কম্প্যাক্ট টেক্সট মেসেজ বা শ্রেণী পরিবর্তন আর সম্পাদনা ট্যাগ দিয়ে এম্বেড করে দেখানো যে।</p>\n<p>রিসেন্ট চেঞ্জেস ওয়েবহুকের জন্যে আবশ্যকতা:</p>\n<ul>\n<li>উইকিটি <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">মিডিয়াউইকি ১.৩০</a> বা উঁচুতে চলতে হবে।</li>\n<li>সিস্টেম মেসেজ <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code>কে ডিসকর্ড সার্ভার ID <code class=\"user-select\" id=\"server-id\"></code> হতে হবে।</li>\n</ul>",
         "form": {
+            "avatar": "ওয়েবহুক ছবি:",
+            "avatar_preview": "প্রিভিউ",
             "channel": "চ্যানেল:",
             "confirm": "আপনি কি সত্যি রিসেন্ট চেঞ্জেস ওয়েবহুকটিকে ডিলিট করতে চান?",
             "display": "ডিস্প্লে মোড:",
@@ -150,6 +161,7 @@
             "feeds": "ফীড-আধারিত পরিবর্তন:",
             "feeds_only": "শুধু ফীড-আধারিত পরিবর্তন:",
             "lang": "ভাষা:",
+            "name": "ওয়েবহুকের নাম:",
             "new": "নতুন রিসেন্ট চেঞ্জেস ওয়েবহুক",
             "select_channel": "-- একটি ট্যানেল বাছুন --",
             "wiki": "উইকি:",
@@ -159,12 +171,12 @@
     },
     "selector": {
         "desc": "এগুলি সে সব সার্ভার যেখানে আপনি সেটিংগ্স বদলাতে পারেন কেনকি আপনার কাছে [Manage Server]($1) অনুমতি আছে। অনুগ্রহ করে একটি সার্ভার বাছুন:",
-        "invite": "উইকি-ব'ট এখনো $1এর সদস্য নয়, কিন্তু আপনি [উইকি-ব'টকে ইনভাইট]($2) করতে পারেন।",
+        "invite": "উইকি-বট এখনো $1এর সদস্য নয়, কিন্তু আপনি [উইকি-বটকে আমন্ত্রণ]($2) করতে পারেন।",
         "none": "আপনার কাছে এখন কোনো সার্ভারে [Manage Server]($1) অনুমতি নেই, আপনি কি সঠিক একাউন্ট দিয়ে ল'গইন করেছেন?",
         "switch": "একাউন্ট বদলান",
         "title": "সার্ভার বাছুন",
-        "with": "উইকি-ব'ট থাকা সার্ভারসমূহ",
-        "without": "উইকি-ব'ট ছাড়া সার্ভারসমূহ"
+        "with": "উইকি-বট থাকা সার্ভারসমূহ",
+        "without": "উইকি-বট ছাড়া সার্ভারসমূহ"
     },
     "settings": {
         "desc": "এগুলি $1এর সেটিংস:",
@@ -210,6 +222,8 @@
             "confirm": "আপনি কি সত্যিই ভেরিফিকেশনটি ডিলিট করতে চান?",
             "editcount": "সর্বনিম্ন সম্পাদনার মাত্রা:",
             "entry": "ভেরিফিকেশন #$1",
+            "flag_logall": "অসফল যাচাইকরণ লগ করুন:",
+            "flag_private": "ব্যক্তিগত কমান্ডের সাড়াসমূহ:",
             "logging": "লগ করার জন্যে চ্যানেল:",
             "match": "প্রয়োজনীয়তা না পাওয়ার ম্যাসেজ:",
             "match_placeholder": "ডিসকর্ড ট্যাগ মিলার সত্তেও কোনো প্রয়োজনীয়তা না পাওয়ার ম্যাসেজ।",
@@ -223,6 +237,8 @@
             "postcount_or": "শুধু সম্পাদনা বা শুধু পোস্টের মাত্রার ব্যবহার করুন।",
             "rename": "ব্যবহারকারীকে রিনেম করতে হবে:",
             "role": "রোল:",
+            "role_add": "যোগ",
+            "role_remove": "অপসারণ",
             "select_channel": "-- একটি ট্যানেল বাছুন --",
             "select_role": "-- একটি রোল বাছেন --",
             "success": "সফলতার সূচনা:",

+ 37 - 0
dashboard/i18n/de.json

@@ -102,6 +102,27 @@
             "text": "Die Beschränkungen für den Befehl $1 können nicht geändert werden, ohne dass Verifizierungen eingerichtet wurden.",
             "title": "Verifizierungen nicht eingerichtet!"
         },
+        "oauth": {
+            "text": "Dein Wiki-Account wurde erfolgreich mit Wiki-Bot verbunden.",
+            "title": "Account erfolgreich verbunden!"
+        },
+        "oauthfail": {
+            "text": "Dein Wiki-Account konnte nicht verbunden werden, versuche es noch einmal.",
+            "title": "Verbindung fehlgeschlagen!"
+        },
+        "oauthlogin": {
+            "text": "Bitte melde dich an, wenn du nicht möchtest, das Wiki-Bot deinen Wiki-Account speichert.",
+            "title": "Dein verbundener Wiki-Account wurde gespeichert!"
+        },
+        "oauthother": {
+            "note": "Accounts wechseln.",
+            "text": "Bitte beachte, dass dein Wiki-Account mit einem anderen Discord-Account verbunden wurde, als der mit dem du gerade angemeldet bist.",
+            "title": "Account erfolgreich verbunden!"
+        },
+        "oauthverify": {
+            "text": "Dein Wiki-Account wurde erfolgreich verifiziert.",
+            "title": "Account erfolgreich verifiziert!"
+        },
         "readonly": {
             "text": "Du kannst die Einstellungen aktuell nur sehen, jedoch nicht ändern.",
             "title": "Nur-Lese-Modus!"
@@ -144,6 +165,21 @@
             "title": "Wiki ist gesperrt!"
         }
     },
+    "oauth": {
+        "desc": "Dies sind deine OAuth2-Einstellungen zum Verbinden deiner Wiki-Accounts:",
+        "failed": "Fehler beim Laden der OAuth2-Einstellungen!",
+        "form": {
+            "connect": "Verbinde Account.",
+            "connected": "Verbunden",
+            "current": "Aktueller Status:",
+            "default": "Wiki-Account Verbindungen",
+            "disable": "Speichere meinen Account nicht!",
+            "disabled": "Deaktiviert",
+            "disconnect": "Account trennen.",
+            "enable": "Speichere meinen Account.",
+            "unconnected": "Nicht verbunden"
+        }
+    },
     "rcscript": {
         "desc": "Dies sind die Letzte Änderungen-WebHooks für $1:",
         "explanation": "<h2>Letzte Änderungen-WebHook</h2>\n<p>Wiki-Bot ist in der Lage einen Letzte Änderungen-WebHook basierend auf <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a> auszuführen. Die letzten Änderungen können als kompakte Textnachrichten mit Inline-Links oder als Einbettungen mit Bearbeitungsmarkierungen und Kategorieänderungen angezeigt werden.</p>\n<p>Voraussetzungen um einen Letzte Änderungen-WebHook hinzuzufügen:</p>\n<ul>\n<li>Das Wiki benötigt mindestens <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a>.</li>\n<li>Die Systemnachricht <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> muss mit der Discord-Server-ID <code class=\"user-select\" id=\"server-id\"></code> übereinstimmen.</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "Du hast derzeit die [Server verwalten]($1)-Berechtigung auf keinem Server, bist du mit dem richtigen Account angemeldet?",
         "switch": "Accounts wechseln",
         "title": "Serverauswahl",
+        "user": "Wiki-Accounts",
         "with": "Server mit Wiki-Bot",
         "without": "Server ohne Wiki-Bot"
     },

+ 37 - 0
dashboard/i18n/es.json

@@ -102,6 +102,27 @@
             "text": "Las restricciones para el comando $1 no se pueden cambiar sin que se configuren las verificaciones.",
             "title": "¡Las verificaciones no están configuradas!"
         },
+        "oauth": {
+            "text": "Tu cuenta wiki se ha conectado correctamente con Wiki-Bot.",
+            "title": "¡Cuenta conectada correctamente!"
+        },
+        "oauthfail": {
+            "text": "No se pudo conectar tu cuenta wiki, por favor inténtalo de nuevo.",
+            "title": "¡La conexión falló!"
+        },
+        "oauthlogin": {
+            "text": "Por favor, inicia sesión si no deseas que Wiki-Bot recuerde tu cuenta wiki.",
+            "title": "¡Tu cuenta wiki conectada se ha guardado!"
+        },
+        "oauthother": {
+            "note": "Cambiar cuentas.",
+            "text": "Por favor, ten en cuenta que tu cuenta wiki se conectó con una cuenta de Discord diferente a la que actualmente estas conectado.",
+            "title": "¡Cuenta conectada correctamente!"
+        },
+        "oauthverify": {
+            "text": "Tu cuenta wiki se ha verificado correctamente.",
+            "title": "¡Cuenta verificada correctamente!"
+        },
         "readonly": {
             "text": "Actualmente solo puedes ver tu configuración, pero no cambiarla.",
             "title": "¡Base de datos de solo lectura!"
@@ -144,6 +165,21 @@
             "title": "¡Wiki bloqueado!"
         }
     },
+    "oauth": {
+        "desc": "Estas son tus configuraciones de OAuth2 para conectar cuentas wiki:",
+        "failed": "¡No se pudo cargar la configuración de OAuth2!",
+        "form": {
+            "connect": "Conectar cuenta.",
+            "connected": "Conectado",
+            "current": "Estado Actual:",
+            "default": "Conexiones de Cuenta Wiki",
+            "disable": "¡No recuerdes mi cuenta!",
+            "disabled": "Desactivado",
+            "disconnect": "Desconectar cuenta.",
+            "enable": "Recuerda mi cuenta.",
+            "unconnected": "Desconectado"
+        }
+    },
     "rcscript": {
         "desc": "Estos son los webhooks de cambios recientes para $1:",
         "explanation": "<h2>Webhook de Cambios Recientes</h2>\n<p>Wiki-Bot puede ejecutar un webhook de cambios recientes basado en <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>. Los cambios recientes se pueden mostrar en mensajes de texto compactos con enlaces en la misma línea o mensajes adjuntos con etiquetas de edición y cambios de categoría.</p>\n<p>Requisitos para agregar un webhook de cambios recientes:</p>\n<ul>\n<li>El wiki necesita ejecutarse en <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a> o superior.</li>\n<li>El mensaje del sistema <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> debe configurarse con el ID del servidor Discord <code class=\"user-select\" id=\"server-id\"></code>.</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "Actualmente no tienes el permiso [Administrar Servidor]($1) en ningún servidor, ¿has iniciado sesión en la cuenta correcta?",
         "switch": "Cambiar Cuenta",
         "title": "Selector de Servidor",
+        "user": "Cuentas Wiki",
         "with": "Servidores con Wiki-Bot",
         "without": "Servidores sin Wiki-Bot"
     },

+ 37 - 0
dashboard/i18n/hi.json

@@ -102,6 +102,27 @@
             "text": "$1 कमांड की सीमाओं को वेरिफिकेशन सेट किए बिना बदला नहीं जा सकता।",
             "title": "वेरिफिकेशन सेट नहीं किए गए हैं!"
         },
+        "oauth": {
+            "text": "आपके विकि को सफलतापूर्वक Wiki-Bot से जोड़ दिया गया है।",
+            "title": "अकाउंट को सफलतापूर्वक जोड़ दिया गया है!"
+        },
+        "oauthfail": {
+            "text": "आपके विकि को नहीं जोड़ा जा सका। कृपया दोबारा कोशिश करें।",
+            "title": "जोड़ने का प्रयास असफल!"
+        },
+        "oauthlogin": {
+            "text": "अगर आप नहीं चाहते कि Wiki-Bot आपके विकि अकाउंट को याद रखे, कृपया लॉग-इन करें।",
+            "title": "आपके जोड़े गए विकि अकाउंट को सहेज दिया गया है!"
+        },
+        "oauthother": {
+            "note": "अकाउंट बदलें।",
+            "text": "कृपया ध्यान रखें कि आपके विकि अकाउंट को एक ऐसे डिस्कॉर्ड अकाउंट से जोड़ा गया है जिससे आपने वर्तमान में लॉग-इन नहीं किया हुआ है।",
+            "title": "अकाउंट को सफलतापूर्वक जोड़ दिया गया है!"
+        },
+        "oauthverify": {
+            "text": "आपके विकि अकाउंट को सफलतापूर्वक वेरिफाई कर दिया गया है।",
+            "title": "अकाउंट सफलतापूर्वक वेरिफाई किया गया है!"
+        },
         "readonly": {
             "text": "आप इस समय अपने सेटिंग्स को बस देख सकते हैं, पर इसे बदल नहीं सकतें।",
             "title": "रीड-ऑनली डेटाबेस!"
@@ -144,6 +165,21 @@
             "title": "विकी ब्लॉक्ड है!"
         }
     },
+    "oauth": {
+        "desc": "ये विकि अकाउंट जोड़ने के लिए आपके OAuth2 सेटिंग्स हैं:",
+        "failed": "OAuth2 सेटिंग्स लोड नहीं किए जा सके!",
+        "form": {
+            "connect": "अकाउंट जोड़ें।",
+            "connected": "जोड़े गए",
+            "current": "वर्तमान स्थिति:",
+            "default": "जोड़े गए विकि अकाउंट",
+            "disable": "मेरे अकाउंट को याद मत रखना!",
+            "disabled": "अक्षम",
+            "disconnect": "अकाउंट अलग करें।",
+            "enable": "मेरे अकाउंट को याद रखें।",
+            "unconnected": "जोड़े नहीं गए"
+        }
+    },
     "rcscript": {
         "desc": "ये $1 के लिए रीसेंट चेंजेस वेबहुक हैं:",
         "explanation": "<h2>रीसेंट चेंजेस वेबहुक</h2>\n<p>विकी-बॉट <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a> पर आधारित एक रीसेंट चेंजेस वेबहुक चला सकता है। रिसेंट चेंजेस को इनलाइन लिंक के साथ कॉम्पैक्ट मैसेज या सम्पादना टैग और एम्बेड वाले मैसेजों में दिखाया जा सकता है।</p>\n<p>रीसेंट चेंजेस वेबहुक बनाने की आवश्यकताएँ:</p>\n<ul>\n<li>विकी को <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">मीडियाविकि १.३०</a> या इसके ऊपर होना होगा।</li>\n<li>सिस्टम मैसेज <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> को डिस्कॉर्ड सर्वर ID <code class=\"user-select\" id=\"server-id\"></code> पर सेट करने होगा।</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "आपके पास इस समय किसी भी सर्वर पर [Manage Server]($1) अनुमति नहीं है, क्या आप सही अकाउंट पर लॉग्डइन हैं?",
         "switch": "अकाउंट बदलें",
         "title": "सर्वर चुनिए",
+        "user": "विकि अकाउंट",
         "with": "Wiki-Bot वाले सर्वर",
         "without": "बिना Wiki-Bot के सर्वर"
     },

+ 267 - 2
dashboard/i18n/it.json

@@ -7,19 +7,284 @@
         " "
     ],
     "general": {
+        "botlist": {
+            "text": "Aiuta altri utenti a trovare Wiki-Bot votandolo nelle liste di bot:",
+            "title": "Liste di bot"
+        },
         "delete": "Cancella",
         "invite": "Invita Wiki-Bot",
+        "language": "Cambia lingua",
+        "login": "Accedi",
         "logout": "Esci",
         "rcscript": "Ultime modifiche",
         "refresh": "Aggiorna lista dei server",
         "save": "Salva",
         "selector": "Selettore server",
         "settings": "Impostazioni",
+        "slash": "Comandi slash",
         "support": "Server di supporto",
+        "theme-dark": "Usa il tema scuro",
+        "theme-light": "Usa il tema chiaro",
         "title": "Impostazioni Wiki-Bot",
-        "verification": "Verifiche"
+        "verification": "Verifiche",
+        "welcome": "<h2>Benvenuto sulla Dashboard di Wiki-Bot.</h2>\n<p>Wiki-Bot è un bot Discord creato per collegare i server Discord e le wiki di MediaWiki. Aiuta nella creazione di link alle pagine delle wiki, nel verificare gli utenti della wiki, nell'informare delle modifiche più recenti e altro. <a href=\"https://wiki.wikibot.de/wiki/Wiki-Bot_Wiki\" target=\"_blank\">[Maggiori informazioni]</a></p>\n<p>Qui puoi modificare diverse impostazioni del bot per i server sui quali hai il permesso Gestire Server. Per iniziare, dovrai autenticare il tuo account Discord, cosa che puoi fare con questo pulsante:</p>"
+    },
+    "indexjs": {
+        "avatar": {
+            "content_type": "Il link fornito ha $1 come tipo di contenuto, ma solo i seguenti tipi di contenuto sono permessi:",
+            "invalid_url": "Non è stato possibile risolvere l'URL ad un file immagine valido."
+        },
+        "invalid": {
+            "note_http": "Il sito web fornito non ha un certificato TLS/SSL valido! Per motivi di sicurezza, sono supportate solo wiki che usano HTTPS.",
+            "note_private": "La wiki fornita è privata!",
+            "note_timeout": "Il link fornito ha impiegato troppo tempo a rispondere!",
+            "text": "Non è stato possibile risolvere l'URL ad un sito MediaWiki valido!",
+            "title": "Wiki invalida!"
+        },
+        "outdated": {
+            "text": "Il webhook delle ultime modifiche richiede almeno MediaWiki 1.30!",
+            "title": "Versione di MediaWiki obsoleta!"
+        },
+        "prefix": {
+            "backslash": "Il prefisso non può includere barre rovesciate (“\\”)!",
+            "code": "Il prefisso non può includere codice markdown!",
+            "space": "Il prefisso non può includere spazi!"
+        },
+        "sysmessage": {
+            "text": "La pagina $1 deve corrispondere al server id $2.",
+            "title": "Il messaggio di sistema non corrisponde!"
+        },
+        "valid": {
+            "MediaWiki": "Attenzione: Richiede almeno $1 per le funzionalità complete.",
+            "title": "Questa wiki è valida e può essere utilizzata!"
+        }
+    },
+    "notice": {
+        "error": {
+            "text": "Si è riscontrato un errore, per favore riprova di nuovo.",
+            "title": "Errore sconosciuto!"
+        },
+        "invalidusergroup": {
+            "text": "Il nome del gruppo utente era troppo lungo o nei hai forniti troppi.",
+            "title": "Gruppo utente invalido!"
+        },
+        "loginfail": {
+            "text": "Si è riscontrato un errore durante il login, per favore prova di nuovo.",
+            "title": "Login fallito!"
+        },
+        "logout": {
+            "text": "Sei uscito con successo. Per cambiare qualsiasi impostazioni devi accedere nuovamente.",
+            "title": "Uscito con successo!"
+        },
+        "missingperm": {
+            "text": "Tu o il Wiki-Bot non avete il permesso $1 per questa funzione.",
+            "title": "Permesso mancante!"
+        },
+        "mwversion": {
+            "text": "Richiede almeno MediaWiki 1.30, trovato $1 su $2.",
+            "title": "Versione di MediaWiki obsoleta!"
+        },
+        "nochange": {
+            "text": "Le impostazioni corrispondono alle attuali configurazioni di default.",
+            "title": "Salvataggio fallito!"
+        },
+        "nosettings": {
+            "note": "Modifica le impostazioni.",
+            "text": "Per favore definisci prima le impostazioni del server.",
+            "title": "Il server non è ancora impostato!"
+        },
+        "noslash": {
+            "note": "Abilita i comandi slash.",
+            "text": "I comandi slash di Wiki-Bot non sono abilitati su questo server.",
+            "title": "I comandi slash non sono abilitati!"
+        },
+        "noverify": {
+            "text": "Le restrizioni per il comando $1 non possono essere modificate senza aver impostato le verifiche.",
+            "title": "Le verifiche non sono impostate!"
+        },
+        "oauth": {
+            "text": "Il tuo account sulla wiki è stato connesso con successo da Wiki-Bot.",
+            "title": "Account connesso con successo!"
+        },
+        "oauthfail": {
+            "text": "Non è stato possibile connettere il tuo account sulla wiki, per favore prova di nuovo.",
+            "title": "Connessione fallita!"
+        },
+        "oauthlogin": {
+            "text": "Per favore accedi se non vuoi che Wiki-Bot ricordi il tuo account sulla wiki.",
+            "title": "Il tuo account sulla wiki connesso è stato salvato!"
+        },
+        "oauthother": {
+            "note": "Cambia account.",
+            "text": "Per favore nota che il tuo account sulla wiki è stato connesso con un account Discord diverso da quello con cui sei attualmente loggato.",
+            "title": "Account connesso con successo!"
+        },
+        "oauthverify": {
+            "text": "Il tuo account sulla wiki è stato verificato con successo.",
+            "title": "Account verificato con successo!"
+        },
+        "readonly": {
+            "text": "Puoi attualmente solo vedere le tue impostazioni, ma non cambiarle.",
+            "title": "Database in sola lettura!"
+        },
+        "refresh": {
+            "text": "La lista dei server è stata aggiornata con successo.",
+            "title": "Aggiornata con successo!"
+        },
+        "refreshfail": {
+            "text": "Non è stato possibile aggiornare la tua lista di server, per favore prova di nuovo.",
+            "title": "Aggiornamento fallito!"
+        },
+        "save": {
+            "text": "Le impostazioni sono state aggiornate con successo.",
+            "title": "Impostazioni salvate!"
+        },
+        "savefail": {
+            "note_http": "Il sito web fornito non ha un certificato TLS/SSL valido! Per motivi di sicurezza, sono supportate solo wiki che usano HTTPS.",
+            "note_private": "La wiki fornita è private!",
+            "note_timeout": "Il link fornito ha impiegato troppo tempo a rispondere!",
+            "text": "Non è stato possibile salvare le impostazioni, per favore riprova di nuovo.",
+            "title": "Salvataggio fallito!"
+        },
+        "sysmessage": {
+            "text": "La pagina $1 deve corrispondere al server id $2.",
+            "title": "Il messaggio di sistema non corrisponde!"
+        },
+        "unauthorized": {
+            "text": "Per favore accedi prima di poter modificare qualsiasi impostazione.",
+            "title": "Accesso non eseguito!"
+        },
+        "webhookfail": {
+            "note": "Il webhook Discord non può essere modificato!",
+            "text": "Le impostazioni sono state aggiornate solo parzialemente.",
+            "title": "Impostazioni parzialmente salvate!"
+        },
+        "wikiblocked": {
+            "note": "Motivazione:",
+            "text": "$1 è stata bloccata dall'essere aggiunta come webhook delle ultime modifiche.",
+            "title": "La wiki è bloccata!"
+        }
+    },
+    "oauth": {
+        "desc": "Queste sono le tue impostazioni OAuth2 per connettere gli account della wiki:",
+        "failed": "Caricamento delle impostazioni OAuth2 fallito!",
+        "form": {
+            "connect": "Connetti l'account.",
+            "connected": "Connesso",
+            "current": "Stato attuale:",
+            "default": "Connessioni Acccount Wiki",
+            "disable": "Non ricordo il mio account!",
+            "disabled": "Disabilitato",
+            "disconnect": "Disconnetti l'account.",
+            "enable": "Ricorda il mio account.",
+            "unconnected": "Sconnesso"
+        }
+    },
+    "rcscript": {
+        "desc": "Questi sono i webhook delle ultime modifiche per $1:",
+        "explanation": "<h2>Webhook Ultime Modifiche</h2>\n<p>Wiki-Bot è in grado di runnare un webhook delle ultime modifiche basato su <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>. Le ultime modifiche possono essere visualizzate come messaggi di testo compatti con link in linea oppure messaggi incorporati con tag di modifica e cambi di categoria.</p>\n<p>Requisiti per aggiungere un webhook delle ultime modifiche:</p>\n<ul>\n<li>La wiki deve utilizzare <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a> o successiva.</li>\n<li>Il messaggio di sistema <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> deve essere impostato con l'id del server Discord <code class=\"user-select\" id=\"server-id\"></code>.</li>\n</ul>",
+        "form": {
+            "avatar": "Avatar del webhook:",
+            "avatar_preview": "Anteprima",
+            "channel": "Canale:",
+            "confirm": "Vuoi davvero eliminare il webhook delle ultime modifiche?",
+            "display": "Modalità di visualizzazione:",
+            "display_compact": "Messaggi di testo compatti con link in linea.",
+            "display_diff": "Messaggi incorporati con anteprima delle immagini e differenze di modifica.",
+            "display_embed": "Messaggi incorporati con tag di modifica e cambi di categoria.",
+            "display_image": "Messaggi incorporati con anteprima delle immagini.",
+            "entry": "Webhook delle ultime modifiche #$1",
+            "feeds": "Attività nei feed:",
+            "feeds_only": "Solo attività nei feed:",
+            "lang": "Lingua:",
+            "name": "Nome del webhook:",
+            "new": "Nuovo webhook delle ultime modifiche",
+            "select_channel": "-- Seleziona un canale --",
+            "wiki": "Wiki:",
+            "wiki_check": "Controlla la wiki"
+        },
+        "new": "Nuovo webhook"
     },
     "selector": {
-        "title": "Selettore server"
+        "desc": "Questa è una lista di tutti i server di cui puoi modificare le impostazioni perché hai il permesso [Gestire Server]($1). Per favore seleziona un server:",
+        "invite": "Wiki-Bot non è ancora un membro di $1, ma puoi [invitarlo]($2).",
+        "none": "Attualmente non hai il permesso [Gestire Server]($1) su alcun server, sei loggato con l'account corretto?",
+        "switch": "Cambia account",
+        "title": "Selettore server",
+        "user": "Account della wiki",
+        "with": "Server con Wiki-Bot",
+        "without": "Server senza Wiki-Bot"
+    },
+    "settings": {
+        "desc": "Queste sono le impostazioni per $1:",
+        "failed": "Caricamento delle impostazioni fallito!",
+        "form": {
+            "channel": "Canale:",
+            "confirm": "Vuoi davvero eliminare la sovrascrittura del canale?",
+            "default": "Impostazioni globali del server",
+            "inline": "Comandi in linea:",
+            "lang": "Lingua:",
+            "new": "Nuova sovrascrittura canale",
+            "overwrite": "Impostazioni $1",
+            "prefix": "Prefisso:",
+            "prefix_space": "Prefisso finisce con uno spazio:",
+            "role": "Ruolo minimo:",
+            "select_channel": "-- Seleziona un canale --",
+            "wiki": "Wiki di default:",
+            "wiki_check": "Controlla la wiki"
+        },
+        "new": "Nuova sovrascrittura canale"
+    },
+    "slash": {
+        "desc": "Questi sono i comandi slash per $1:",
+        "explanation": "<h2>Comandi slash</h2>\n<p>L'uso di specifici comandi slash può essere ristretto ad alcuni ruoli. Puoi farlo per i comandi slash di Wiki-Bot qui.</p>",
+        "form": {
+            "add": "Aggiungi",
+            "allow": "Acconsenti",
+            "default": "Default",
+            "default_allow": "Di default questo comando può essere usato da tutti.",
+            "default_deny": "Di default questo comando non può essere usato da tutti.",
+            "deny": "Nega",
+            "entry": "Comando $1",
+            "role": "Ruolo:",
+            "select_role": "-- Seleziona un ruolo --"
+        }
+    },
+    "verification": {
+        "desc": "Queste sono le verifiche per $1:",
+        "explanation": "<h2>Verifica utente</h2>\n<p>Usando il comando <code class=\"prefix\">verify &lt;nome utente wiki&gt;</code>, gli utenti possono confermare di essere uno specifico utente della wiki utilizzando il campo Discord del proprio profilo. Se l'utente corrisponde e le verifiche degli utenti sono impostate sul server, Wiki-Bot assegnerà i ruoli corrispondenti alle verifiche superate dall'utente.</p>\n<p>Ciascuna entrata permette di inserire restrizioni multiple sulle condizioni che un utente deve soddisfare per essere verificato:</p>\n<ul>\n<li>Il canale in cui usare il comando <code class=\"prefix\">verify</code>.</li>\n<li>Il ruolo che si ottiene superando la verifica.</li>\n<li>Il conteggio delle modifiche richiesto sulla wiki per superare la verifica.</li>\n<li>Il gruppo utente di cui è richiesto essere membri sulla wiki per superare la verifica.</li>\n<li>Età dell'account in giorni richiesta per superare la verifica.</li>\n<li>Opzione per cambiare il soprannome dell'utente Discord nel nome utente sulla wiki quando supera la verifica.</li>\n</ul>",
+        "form": {
+            "accountage": "Età dell'account (in giorni):",
+            "channel": "Canale:",
+            "confirm": "Vuoi davvero eliminare questa verifica?",
+            "editcount": "Conteggio delle modifiche minimo:",
+            "entry": "Verifica #$1",
+            "flag_logall": "Registra le verifiche senza successo:",
+            "flag_private": "Risposte al comando private:",
+            "logging": "Canale di log:",
+            "match": "Avviso requisiti mancanti:",
+            "match_placeholder": "Testo in markdown in caso di corrispondenza del tag Discord ma mancanza dei requisiti per i ruoli.",
+            "more": "Aggiungi di più",
+            "new": "Nuova verifica",
+            "notice": "Avvisi di verifica",
+            "postcount": "Conteggio dei post minimo:",
+            "postcount_and": "Richiedi sia il conteggio delle modifiche sia dei post.",
+            "postcount_both": "Richiedi un conteggio combinato di modifiche e post.",
+            "postcount_fandom": "Solo sulle wiki di Fandom:",
+            "postcount_or": "Richiedi o il conteggio delle modifiche o quello dei post.",
+            "rename": "Rinominare gli utenti:",
+            "role": "Ruolo:",
+            "role_add": "Aggiungi",
+            "role_remove": "Rimuovi",
+            "select_channel": "-- Seleziona un canale --",
+            "select_role": "-- Seleziona un ruolo --",
+            "success": "Avviso di successo:",
+            "success_placeholder": "Testo in markdown in caso di successo della verifica.",
+            "usergroup": "Gruppo utente della wiki:",
+            "usergroup_and": "Richiedi tutti i gruppi utente:"
+        },
+        "help_notice": "<p>Gli avvisi personalizzati supportano alcune semplici funzioni e variabili.</p>\n<ul>\n<li><code class=\"form-button user-select\">$editcount</code> – L'attuale conteggio delle modifiche dell'utente.</li>\n<li><code class=\"form-button user-select\">$accountage</code> – L'attuale età dell'account dell'utente in giorni.</li>\n<li><code class=\"form-button user-select\">$postcount</code> – L'attuale conteggio dei post di discussione dell'utente (solo per le wiki di Fandom).</li>\n<li><code class=\"form-button user-select\" data-after=\" }}\" data-before=\"{{#expr: \">{{#expr: 1+1}}</code> – Ritorna il risultato di un'espressione.\n<ul>\n<li>Supporta solo la somma <code class=\"form-button user-select\">+</code> e la sottrazione <code class=\"form-button user-select\">-</code>.</li>\n</ul></li>\n<li><code class=\"form-button user-select\" data-after=\" |  |  }}\" data-before=\"{{#ifexpr: \">{{#ifexpr: 1 &gt; 1 | <i>if true</i> | <i>if false</i> }}</code> – Ritorna il testo a seconda del risultato dell'espressione.\n<ul>\n<li>Supporta <code class=\"form-button user-select\">&lt;</code>, <code class=\"form-button user-select\">&gt;</code>, <code class=\"form-button user-select\">=</code>, <code class=\"form-button user-select\">&lt;=</code>, <code class=\"form-button user-select\">&gt;=</code>, <code class=\"form-button user-select\">!=</code>, <code class=\"form-button user-select\">&lt;&gt;</code> così come <code class=\"form-button user-select\">e</code>, <code class=\"form-button user-select\">o</code>.</li>\n</ul></li>\n</ul>",
+        "new": "Nuova verifica",
+        "notice": "Avvisi di verifica"
     }
 }

+ 37 - 0
dashboard/i18n/ja.json

@@ -102,6 +102,27 @@
             "text": "認証がセットアップされていないと、 $1 コマンドの制限は変更できません。",
             "title": "認証がセットアップされていません!"
         },
+        "oauth": {
+            "text": "あなたのWikiアカウントとWiki-Botの連携が完了しました。",
+            "title": "アカウントの連携が完了しました!"
+        },
+        "oauthfail": {
+            "text": "あなたのWikiアカウントと連携できませんでした。もう一度試してください。",
+            "title": "連携に失敗しました!"
+        },
+        "oauthlogin": {
+            "text": "Wiki-BotにあなたのWikiアカウントを覚えさせたくない場合は、ログインしてください。",
+            "title": "あなたの連携したWikiアカウントが保存されました!"
+        },
+        "oauthother": {
+            "note": "アカウントを切り替える。",
+            "text": "Wikiアカウントは、現在ログインしているアカウントとは別のDiscordアカウントと連携されています。",
+            "title": "アカウントの連携が完了しました!"
+        },
+        "oauthverify": {
+            "text": "あなたのWikiアカウントの認証が完了しました。",
+            "title": "アカウントの認証が完了しました!"
+        },
         "readonly": {
             "text": "現在、自分の設定を確認することはできますが、変更することはできません。",
             "title": "読み取り専用のデータベースです!"
@@ -144,6 +165,21 @@
             "title": "Wikiがブロックされています!"
         }
     },
+    "oauth": {
+        "desc": "Wikiアカウントと連携するためのOAuth2の設定です:",
+        "failed": "OAuth2設定の読み込みに失敗しました!",
+        "form": {
+            "connect": "アカウント連携。",
+            "connected": "連携済",
+            "current": "現在の状況:",
+            "default": "Wikiアカウントの連携",
+            "disable": "自分のアカウントを忘れた!",
+            "disabled": "無効",
+            "disconnect": "アカウントを切断する。",
+            "enable": "アカウントを記憶する。",
+            "unconnected": "未接続"
+        }
+    },
     "rcscript": {
         "desc": "$1 の最近の更新のウェブフックです。",
         "explanation": "<h2>最近の変更点のウェブフック</h2>\n<p>Wiki-Botは、<a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>をベースにした最近の変更のウェブフックを実行することができます。最近の変更を、直リンク付きの小さなテキストメッセージや、編集されたタグやカテゴリーの変更ができる埋め込みメッセージを表示することができます。</p>\n<p>最近の変更のウェブフックを追加するための要件:</p>\n<ul>\n<li>Wikiは<a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a>以上で動作している必要があります。</li>\n<li>システムメッセージの<code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> にDiscordのサーバーID <code class=\"user-select\" id=\"server-id\"></code> を設定する必要があります。</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "現在、参加しているどのサーバーにも[サーバー管理]($1)の権限がありませんが、正しいアカウントでログインしていますか?",
         "switch": "アカウントの切り替え",
         "title": "サーバーを選択",
+        "user": "Wikiアカウント",
         "with": "Wiki-Botが参加しているサーバー",
         "without": "Wiki-Botが参加していないサーバー"
     },

+ 37 - 0
dashboard/i18n/pt-br.json

@@ -102,6 +102,27 @@
             "text": "As restrições para o comando $1 não podem ser alteradas sem que as verificações sejam configuradas.",
             "title": "As verificações não foram configuradas!"
         },
+        "oauth": {
+            "text": "Sua conta wiki foi conectada com sucesso ao Wiki-Bot.",
+            "title": "Conta conectada com sucesso!"
+        },
+        "oauthfail": {
+            "text": "Não foi possível conectar sua conta wiki, tente novamente.",
+            "title": "A conexão falhou!"
+        },
+        "oauthlogin": {
+            "text": "Por favor, entre se você não quiser que o Wiki-Bot se lembre da sua conta wiki.",
+            "title": "Sua conta wiki conectada foi salva!"
+        },
+        "oauthother": {
+            "note": "Alterar contas.",
+            "text": "Por favor, note que sua conta wiki foi conectada a uma conta Discord diferente daquela com a qual você está atualmente entrado.",
+            "title": "Conta conectada com sucesso!"
+        },
+        "oauthverify": {
+            "text": "Sua conta wiki foi verificada com sucesso.",
+            "title": "Conta verificada com sucesso!"
+        },
         "readonly": {
             "text": "No momento, você só pode ver suas configurações, mas não pode alterá-las.",
             "title": "Banco de dados somente leitura!"
@@ -144,6 +165,21 @@
             "title": "A wiki está bloqueada!"
         }
     },
+    "oauth": {
+        "desc": "Estas são suas configurações OAuth2 para conectar contas wiki:",
+        "failed": "Falha ao carregar as configurações do OAuth2!",
+        "form": {
+            "connect": "Conectar conta.",
+            "connected": "Conectado",
+            "current": "Status atual:",
+            "default": "Conexões de conta Wiki",
+            "disable": "Não se lembra da minha conta!",
+            "disabled": "Desativado",
+            "disconnect": "Desconectar conta.",
+            "enable": "Lembre-se de minha conta.",
+            "unconnected": "Desconectado"
+        }
+    },
     "rcscript": {
         "desc": "Estes são os webhooks de mudanças recentes para $1:",
         "explanation": "<h2>Webhook de mudanças recentes</h2>\n<p>O Wiki-Bot é capaz de executar um webhook de mudanças recentes baseado no <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>. As mudanças recentes podem ser exibidas em mensagens de texto compactas com links embutidos ou mensagens incorporadas com etiquetas de edição e mudanças de categoria.</p>\n<p>Requisitos para adicionar um webhook de mudanças recentes:</p>\n<ul>\n<li>A wiki precisa estar no <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a> ou superior.</li>\n<li>A mensagem de sistema <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code> precisa ser definida com o ID do servidor do Discord <code class=\"user-select\" id=\"server-id\"></code>.</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "No momento, você não tem a permissão de [gerenciar servidor]($1) em nenhum servidor. Você está conectado à conta correta?",
         "switch": "Trocar conta",
         "title": "Seletor de servidor",
+        "user": "Contas Wiki",
         "with": "Servidores com Wiki-Bot",
         "without": "Servidores sem Wiki-Bot"
     },

+ 9 - 0
dashboard/i18n/sr.json

@@ -0,0 +1,9 @@
+{
+    "fallback": [
+        "en",
+        " ",
+        " ",
+        " ",
+        " "
+    ]
+}

+ 20 - 0
dashboard/i18n/sv.json

@@ -6,6 +6,26 @@
         " ",
         " "
     ],
+    "general": {
+        "botlist": {
+            "text": "Hjälp andra att hitta Wiki-Bot genom att rösta på den i dessa bot-listor:",
+            "title": "Bot-listor"
+        },
+        "delete": "Radera",
+        "invite": "Bjud in Wiki-Bot",
+        "language": "Ändra Språk",
+        "login": "Logga in",
+        "logout": "Logga ut",
+        "rcscript": "Senaste ändringarna",
+        "refresh": "Uppdatera serverlistan",
+        "save": "Spara",
+        "selector": "Serverväljare",
+        "settings": "Inställningar",
+        "slash": "Slash-kommandon",
+        "support": "Support-server",
+        "theme-dark": "Använd mörkt tema",
+        "theme-light": "Använd ljust tema"
+    },
     "rcscript": {
         "form": {
             "lang": "Språk:"

+ 37 - 0
dashboard/i18n/tr.json

@@ -102,6 +102,27 @@
             "text": "$1 komutunun kısıtlamaları doğrulamalar kurulmadan önce değiştirilemez.",
             "title": "Doğrulamalar kurulmamış!"
         },
+        "oauth": {
+            "text": "Viki hesabın başarıyla Wiki-Bot ile bağlandı.",
+            "title": "Hesap başarıyla bağlandı!"
+        },
+        "oauthfail": {
+            "text": "Viki hesabın bağlanamadı, lütfen tekrar dene.",
+            "title": "Bağlanılamadı!"
+        },
+        "oauthlogin": {
+            "text": "Eğer Wiki-Bot'un viki hesabını hatırlamasını istemiyorsan lütfen giriş yap.",
+            "title": "Bağlanan viki hesabın başarıyla kaydedildi!"
+        },
+        "oauthother": {
+            "note": "Hesap değiştir.",
+            "text": "Lütfen viki hesabının giriş yapmış olduğun Discord hesabından farklı bir hesap ile bağlı olduğunu unutma.",
+            "title": "Hesap başarıyla bağlandı!"
+        },
+        "oauthverify": {
+            "text": "Viki hesabın başarıyla doğrulandı.",
+            "title": "Hesap başarıyla doğrulandı!"
+        },
         "readonly": {
             "text": "Şu an ayarları okuyabilirsin ama değiştiremezsin.",
             "title": "Salt okunur veritabanı!"
@@ -144,6 +165,21 @@
             "title": "Viki engellenmiş!"
         }
     },
+    "oauth": {
+        "desc": "Viki hesapların için OAuth2 ayarların:",
+        "failed": "OAuth2 ayarları yüklenemedi!",
+        "form": {
+            "connect": "Bir hesap bağla.",
+            "connected": "Bağlandı",
+            "current": "Mevcut durum:",
+            "default": "Viki Hesabı Bağlantıları",
+            "disable": "Hesabımı hatırlama!",
+            "disabled": "Devre dışı",
+            "disconnect": "Hesabın bağlantısını kopar.",
+            "enable": "Hesabımı hatırla.",
+            "unconnected": "Bağlanmamış"
+        }
+    },
     "rcscript": {
         "desc": "$1 için son değişiklikler webhook'ları:",
         "explanation": "<h2>Son Değişiklikler Webhook'u</h2>\n<p>Wiki-Bot <a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a> bazlı olarak bir son değişiklikler webhook'u sağlayabilir. Son değişiklikler; satıriçi bağlantılar ile kompakt metin mesajları veya düzenleme etiketleri ve kategori değişiklikleri ile gömülü mesajlar olarak görüntülenebilir.</p>\n<p>Bir son değişiklikler webhook'u ekleme gereksinimleri:</p>\n<ul>\n<li>Viki <a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a> veya daha güncel bir versiyona sahip olmalı.</li>\n<li>Sistem mesajı <code class=\"user-select\">MediaWiki:Custom-RcGcDw</code>, Discord sunucu ID'si <code class=\"user-select\" id=\"server-id\"></code> ile eşleşmeli.</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "Halihazırda hiçbir sunucuda [Sunucuyu Yönet]($1) iznine sahip değilsin, doğru hesaba giriş yaptığına emin misin?",
         "switch": "Hesap Değiştir",
         "title": "Sunucu Seçici",
+        "user": "Viki Hesapları",
         "with": "Wiki-Bot'un olduğu sunucular",
         "without": "Wiki-Bot'un olmadığı sunucular"
     },

+ 40 - 3
dashboard/i18n/zh-hans.json

@@ -102,6 +102,27 @@
             "text": "在没有设置验证方式的情况下,无法对命令$1设置使用限制。",
             "title": "未配置验证方式!"
         },
+        "oauth": {
+            "text": "您的wiki账号已成功连接到 Wiki-Bot。",
+            "title": "账号成功连接!"
+        },
+        "oauthfail": {
+            "text": "无法连接到您的wiki账号,请再试一次。",
+            "title": "连接失败!"
+        },
+        "oauthlogin": {
+            "text": "若不希望 Wiki-Bot 记住您的wiki账号,请登录。",
+            "title": "已保存连接到的wiki账号!"
+        },
+        "oauthother": {
+            "note": "切换账号。",
+            "text": "请注意,您的wiki账号连接到的Discord账号与当前登录的不同。",
+            "title": "账号成功连接!"
+        },
+        "oauthverify": {
+            "text": "已成功验证您的wiki账号。",
+            "title": "账号验证成功!"
+        },
         "readonly": {
             "text": "你现在只能查看你的设置,而不能更改。",
             "title": "只读的数据库!"
@@ -144,8 +165,23 @@
             "title": "wiki 已被屏蔽!"
         }
     },
+    "oauth": {
+        "desc": "以下是您连接的wiki账号的 OAuth2 设置:",
+        "failed": "加载 OAuth2 设置失败!",
+        "form": {
+            "connect": "连接账号。",
+            "connected": "已连接",
+            "current": "当前状态:",
+            "default": "wiki账号连接",
+            "disable": "不要记住我的账号!",
+            "disabled": "已禁用",
+            "disconnect": "断开账号连接。",
+            "enable": "记住我的账号。",
+            "unconnected": "未连接"
+        }
+    },
     "rcscript": {
-        "desc": "这些是 $1 的最近更改 webhook:",
+        "desc": "以下是 $1 的最近更改 webhook:",
         "explanation": "<h2>最近更改 webhook</h2>\n<p>Wiki-Bot可以运行基于<a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>的最近更改 webhook。最近更改可以以多种显示模式显示:有行内链接的紧凑型文字信息或有编辑标签和分类更改的嵌入式消息。</p>\n<p>添加最近更改 webhook 的基本要求:</p>\n<ul>\n<li>需要以<a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a>或更高版本运行的 wiki。</li>\n<li>系统消息<code class=\"user-select\">MediaWiki:Custom-RcGcDw</code>需要设置为 Discord 服务器的服务器 ID <code class=\"user-select\" id=\"server-id\"></code>。</li>\n</ul>",
         "form": {
             "avatar": "Webhook 头像:",
@@ -175,11 +211,12 @@
         "none": "你目前并没有任何服务器的[管理服务器]($1)权限,你是否已使用正确的账号登录?",
         "switch": "切换账号",
         "title": "选择服务器",
+        "user": "wiki账号",
         "with": "有 Wiki-Bot 的服务器",
         "without": "没有 Wiki-Bot 的服务器"
     },
     "settings": {
-        "desc": "这些是 $1 的设置:",
+        "desc": "以下是 $1 的设置:",
         "failed": "加载设置失败!",
         "form": {
             "channel": "频道:",
@@ -214,7 +251,7 @@
         }
     },
     "verification": {
-        "desc": "这些是 $1 的验证方式:",
+        "desc": "以下是 $1 的验证方式:",
         "explanation": "<h2>用户验证</h2>\n<p>使用<code class=\"prefix\">verify &lt;wiki用户名&gt;</code>命令,用户可以将他们验证为特定的 wiki 用户,只需要他们在 wiki 个人资料的 Discord 栏位中填入自己的 Discord 即可。若验证结果匹配并且服务器设置了用户验证,那么 Wiki-Bot 就会对应所有与他们相匹配的验证项,给予相应的用户组。</p>\n<p>每条验证项都允许填入多条匹配条件:</p>\n<ul>\n<li>允许使用 <code class=\"prefix\">verify</code> 命令的频道。</li>\n<li>匹配该验证项时授予的身份组。</li>\n<li>匹配该验证项所需的编辑次数。</li>\n<li>匹配该验证项所需的 wiki 用户组。</li>\n<li>匹配该验证项所需的账号注册时长(单位为天)。</li>\n<li>匹配该验证项时是否将 Discord 用户的昵称设置为他们的 wiki 用户名。</li>\n</ul>",
         "form": {
             "accountage": "账号注册时长(天):",

+ 37 - 0
dashboard/i18n/zh-hant.json

@@ -102,6 +102,27 @@
             "text": "在未設定驗證方式時,無法對指令$1設定使用限制。",
             "title": "未設定驗證方式!"
         },
+        "oauth": {
+            "text": "您的wiki帳號已成功連接到Wiki-Bot。",
+            "title": "帳號成功連接!"
+        },
+        "oauthfail": {
+            "text": "無法連接到您的wiki帳號,請再試一次。",
+            "title": "連接失敗!"
+        },
+        "oauthlogin": {
+            "text": "若不希望Wiki-Bot記住您的wiki帳號,請登入。",
+            "title": "已儲存連接到的wiki帳號!"
+        },
+        "oauthother": {
+            "note": "切換帳號。",
+            "text": "請注意,您的wiki帳號所連接的Discord帳號與目前登入的不同。",
+            "title": "帳號成功連接!"
+        },
+        "oauthverify": {
+            "text": "已成功驗證您的wiki帳號。",
+            "title": "帳號驗證成功!"
+        },
         "readonly": {
             "text": "您目前只能查看您的設定,但您無法變更。",
             "title": "唯讀的資料庫!"
@@ -144,6 +165,21 @@
             "title": "wiki已被屏蔽!"
         }
     },
+    "oauth": {
+        "desc": "以下是您連接的wiki帳號的OAuth2設定:",
+        "failed": "載入OAuth2設定失敗!",
+        "form": {
+            "connect": "連接帳號。",
+            "connected": "已連接",
+            "current": "目前狀態:",
+            "default": "wiki帳號連接",
+            "disable": "不要記住我的帳號!",
+            "disabled": "已停用",
+            "disconnect": "解除帳號連接。",
+            "enable": "記住我的帳號。",
+            "unconnected": "未連接"
+        }
+    },
     "rcscript": {
         "desc": "以下為 $1 的近期變更webhook:",
         "explanation": "<h2>近期變更webhook</h2>\n<p>Wiki-Bot能運行基於<a href=\"https://gitlab.com/piotrex43/RcGcDw\" target=\"_blank\">RcGcDw</a>的近期變更webhook。近期變更可以以多種顯示模式顯示:包含行內連結的精簡文字訊息或包含圖片預覽及編輯差異的嵌入式訊息。</p>\n<p>加入近期變更webhook的基本需求:</p>\n<ul>\n<li>需要以<a href=\"https://www.mediawiki.org/wiki/MediaWiki_1.30\" target=\"_blank\">MediaWiki 1.30</a>或更高版本運行的wiki。</li>\n<li>系統訊息<code class=\"user-select\">MediaWiki:Custom-RcGcDw</code>需要設為Discord伺服器的伺服器id <code class=\"user-select\" id=\"server-id\"></code>。</li>\n</ul>",
@@ -175,6 +211,7 @@
         "none": "您目前並沒有任何伺服器的[管理伺服器]($1)權限,您是否已使用正確的帳號登入?",
         "switch": "切換帳號",
         "title": "選擇伺服器",
+        "user": "wiki帳號",
         "with": "有Wiki-Bot的伺服器",
         "without": "沒有Wiki-Bot的伺服器"
     },

+ 1 - 1
dashboard/index.html

@@ -40,7 +40,7 @@
 		</div>
 		<div id="navbar">
 			<a id="selector" href="/" style="width: 230px;">
-				<img class="avatar" src="https://discord.com/assets/f8389ca1a741a115313bede9ac02e2c0.svg" alt="Discord">
+				<img src="/src/discord.svg" alt="Discord">
 				<span>Server Selector</span>
 			</a>
 			<a id="support" href="https://discord.com/invite/r5xDRcy" target="_blank" alt="Help with Wiki-Bot">

+ 38 - 47
dashboard/index.js

@@ -1,22 +1,13 @@
-global.isDebug = ( process.argv[2] === 'debug' );
-
-const http = require('http');
-const pages = require('./oauth.js');
-const dashboard = require('./guilds.js');
-const {db, sessionData, settingsData} = require('./util.js');
-const Lang = require('./i18n.js');
+import http from 'http';
+import fs from 'fs';
+import { extname } from 'path';
+import * as pages from './oauth.js';
+import dashboard from './guilds.js';
+import { posts } from './functions.js';
+import { db, sessionData, settingsData } from './util.js';
+import Lang from './i18n.js';
 const allLangs = Lang.allLangs();
 
-const posts = {
-	user: require('./user.js').post,
-	settings: require('./settings.js').post,
-	verification: require('./verification.js').post,
-	rcscript: require('./rcscript.js').post,
-	slash: require('./slash.js').post
-};
-
-const fs = require('fs');
-const path = require('path');
 const files = new Map([
 	...fs.readdirSync( './dashboard/src' ).map( file => {
 		return [`/src/${file}`, `./dashboard/src/${file}`];
@@ -29,7 +20,7 @@ const files = new Map([
 	} ) : [] )
 ].map( ([file, filepath]) => {
 	let contentType = 'text/html';
-	switch ( path.extname(file) ) {
+	switch ( extname(file) ) {
 		case '.css':
 			contentType = 'text/css';
 			break;
@@ -63,7 +54,6 @@ const server = http.createServer( (req, res) => {
 		if ( state && sessionData.has(state) && settingsData.has(sessionData.get(state).user_id) &&
 		( ( args.length === 5 && ['settings', 'verification', 'rcscript', 'slash'].includes( args[3] ) && /^(?:default|new|notice|\d+)$/.test(args[4])
 		&& settingsData.get(sessionData.get(state).user_id).guilds.isMember.has(args[2]) ) || req.url === '/user' ) ) {
-			if ( process.env.READONLY ) return save_response(`${req.url}?save=failed`);
 			let body = [];
 			req.on( 'data', chunk => {
 				body.push(chunk);
@@ -73,6 +63,7 @@ const server = http.createServer( (req, res) => {
 				res.end('error');
 			} );
 			return req.on( 'end', () => {
+				if ( process.env.READONLY ) return save_response(`${req.url}?save=failed`);
 				var settings = {};
 				Buffer.concat(body).toString().split('&').forEach( arg => {
 					if ( arg ) {
@@ -94,36 +85,36 @@ const server = http.createServer( (req, res) => {
 					}
 				}
 				else return posts[args[3]](save_response, settingsData.get(sessionData.get(state).user_id), args[2], args[4], settings);
-			} );
 
-			/**
-			 * @param {String} [resURL]
-			 * @param {String} [action]
-			 * @param {String[]} [actionArgs]
-			 */
-			function save_response(resURL = '/', action, ...actionArgs) {
-				if ( action === 'REDIRECT' && resURL.startsWith( 'https://' ) ) {
-					res.writeHead(303, {Location: resURL});
-					return res.end();
+				/**
+				 * @param {String} [resURL]
+				 * @param {String} [action]
+				 * @param {String[]} [actionArgs]
+				 */
+				function save_response(resURL = '/', action, ...actionArgs) {
+					if ( action === 'REDIRECT' && resURL.startsWith( 'https://' ) ) {
+						res.writeHead(303, {Location: resURL});
+						return res.end();
+					}
+					var themeCookie = ( req.headers?.cookie?.split('; ')?.find( cookie => {
+						return cookie.split('=')[0] === 'theme' && /^"(?:light|dark)"$/.test(( cookie.split('=')[1] || '' ));
+					} ) || 'dark' ).replace( /^theme="(light|dark)"$/, '$1' );
+					var langCookie = ( req.headers?.cookie?.split('; ')?.filter( cookie => {
+						return cookie.split('=')[0] === 'language' && /^"[a-z\-]+"$/.test(( cookie.split('=')[1] || '' ));
+					} )?.map( cookie => cookie.replace( /^language="([a-z\-]+)"$/, '$1' ) ) || [] );
+					var dashboardLang = new Lang(...langCookie, ...( req.headers?.['accept-language']?.split(',')?.map( lang => {
+						lang = lang.split(';')[0].toLowerCase();
+						if ( allLangs.map.hasOwnProperty(lang) ) return lang;
+						lang = lang.replace( /-\w+$/, '' );
+						if ( allLangs.map.hasOwnProperty(lang) ) return lang;
+						lang = lang.replace( /-\w+$/, '' );
+						if ( allLangs.map.hasOwnProperty(lang) ) return lang;
+						return '';
+					} ) || [] ));
+					dashboardLang.fromCookie = langCookie;
+					return dashboard(res, dashboardLang, themeCookie, sessionData.get(state), new URL(resURL, process.env.dashboard), action, actionArgs);
 				}
-				var themeCookie = ( req.headers?.cookie?.split('; ')?.find( cookie => {
-					return cookie.split('=')[0] === 'theme' && /^"(?:light|dark)"$/.test(( cookie.split('=')[1] || '' ));
-				} ) || 'dark' ).replace( /^theme="(light|dark)"$/, '$1' );
-				var langCookie = ( req.headers?.cookie?.split('; ')?.filter( cookie => {
-					return cookie.split('=')[0] === 'language' && /^"[a-z\-]+"$/.test(( cookie.split('=')[1] || '' ));
-				} )?.map( cookie => cookie.replace( /^language="([a-z\-]+)"$/, '$1' ) ) || [] );
-				var dashboardLang = new Lang(...langCookie, ...( req.headers?.['accept-language']?.split(',')?.map( lang => {
-					lang = lang.split(';')[0].toLowerCase();
-					if ( allLangs.map.hasOwnProperty(lang) ) return lang;
-					lang = lang.replace( /-\w+$/, '' );
-					if ( allLangs.map.hasOwnProperty(lang) ) return lang;
-					lang = lang.replace( /-\w+$/, '' );
-					if ( allLangs.map.hasOwnProperty(lang) ) return lang;
-					return '';
-				} ) || [] ));
-				dashboardLang.fromCookie = langCookie;
-				return dashboard(res, dashboardLang, themeCookie, sessionData.get(state), new URL(resURL, process.env.dashboard), action, actionArgs);
-			}
+			} );
 		}
 	}
 

+ 1 - 1
dashboard/login.html

@@ -23,7 +23,7 @@
 				<p>Here you can change different bot settings for servers you have Manage Server permission on. To begin, you will have to authenticate your Discord account which you can do with this button:</p>
 			</div>
 			<a id="login-button">
-				<img src="https://discord.com/assets/f8389ca1a741a115313bede9ac02e2c0.svg" alt="Discord">
+				<img src="/src/discord.svg" alt="Discord">
 				<span>Login</span>
 			</a>
 			<div id="story">

+ 18 - 14
dashboard/oauth.js

@@ -1,16 +1,20 @@
-const {randomBytes} = require('crypto');
-const cheerio = require('cheerio');
+import { readFileSync } from 'fs';
+import { randomBytes } from 'crypto';
+import cheerio from 'cheerio';
+import Wiki from '../util/wiki.js';
+import { allLangs } from './i18n.js';
+import { got, db, oauth, enabledOAuth2, sessionData, settingsData, oauthVerify, sendMsg, addWidgets, createNotice, hasPerm } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultPermissions} = require('../util/default.json');
-const Wiki = require('../util/wiki.js');
-const allLangs = require('./i18n.js').allLangs().names;
-const {got, db, oauth, enabledOAuth2, sessionData, settingsData, oauthVerify, sendMsg, addWidgets, createNotice, hasPerm} = require('./util.js');
+const allLangNames = allLangs().names;
 
-const file = require('fs').readFileSync('./dashboard/login.html');
+const file = readFileSync('./dashboard/login.html');
 
 /**
  * Let a user login
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('./i18n.js')} dashboardLang - The user language.
+ * @param {import('./i18n.js').default} dashboardLang - The user language.
  * @param {String} theme - The display theme
  * @param {String} [state] - The user state
  * @param {String} [action] - The action the user made
@@ -28,7 +32,7 @@ function dashboard_login(res, dashboardLang, theme, state, action) {
 	if ( theme === 'light' ) $('html').addClass('theme-light');
 	$('<script>').text(`
 		const selectLanguage = '${dashboardLang.get('general.language').replace( /'/g, '\\$&' )}';
-		const allLangs = ${JSON.stringify(allLangs)};
+		const allLangs = ${JSON.stringify(allLangNames)};
 	`).insertBefore('script#langjs');
 	$('head title').text(dashboardLang.get('general.login') + ' – ' + dashboardLang.get('general.title'));
 	$('#login-button span, .channel#login div').text(dashboardLang.get('general.login'));
@@ -398,10 +402,10 @@ function mediawiki_oauth(res, searchParams, user_id) {
 	} );
 }
 
-module.exports = {
-	login: dashboard_login,
-	oauth: dashboard_oauth,
-	refresh: dashboard_refresh,
-	api: dashboard_api,
-	verify: mediawiki_oauth
+export {
+	dashboard_login as login,
+	dashboard_oauth as oauth,
+	dashboard_refresh as refresh,
+	dashboard_api as api,
+	mediawiki_oauth as verify
 };

+ 40 - 23
dashboard/rcscript.js

@@ -1,9 +1,11 @@
-const cheerio = require('cheerio');
+import cheerio from 'cheerio';
+import Lang from '../util/i18n.js';
+import Wiki from '../util/wiki.js';
+import { got, db, sendMsg, createNotice, hasPerm } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultSettings, limit: {rcgcdw: rcgcdwLimit}} = require('../util/default.json');
-const Lang = require('../util/i18n.js');
-const allLangs = Lang.allLangs(true);
-const Wiki = require('../util/wiki.js');
-const {got, db, sendMsg, createNotice, hasPerm} = require('./util.js');
+const allLangs = Lang.allLangs(true).names;
 
 const display_types = [
 	'compact',
@@ -33,8 +35,8 @@ const fieldset = {
 	//+ '</fieldset>',
 	lang: '<label for="wb-settings-lang">Language:</label>'
 	+ '<select id="wb-settings-lang" name="lang" required autocomplete="language">'
-	+ Object.keys(allLangs.names).map( lang => {
-		return `<option id="wb-settings-lang-${lang}" value="${lang}">${allLangs.names[lang]}</option>`
+	+ Object.keys(allLangs).map( lang => {
+		return `<option id="wb-settings-lang-${lang}" value="${lang}">${allLangs[lang]}</option>`
 	} ).join('')
 	+ '</select>'
 	+ '<img id="wb-settings-lang-widget">',
@@ -64,9 +66,9 @@ const fieldset = {
 
 /**
  * Create a settings form
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {String} header - The form header
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  * @param {Object} settings - The current settings
  * @param {Boolean} settings.patreon
  * @param {String} [settings.channel]
@@ -195,10 +197,10 @@ function createForm($, header, dashboardLang, settings, guildChannels, allWikis)
 /**
  * Let a user change recent changes scripts
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {import('./util.js').Guild} guild - The current guild
  * @param {String[]} args - The url parts
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  */
 function dashboard_rcscript(res, $, guild, args, dashboardLang) {
 	db.query( 'SELECT discord.wiki mainwiki, discord.lang mainlang, (SELECT ARRAY_AGG(DISTINCT wiki ORDER BY wiki ASC) FROM discord WHERE guild = $1) allwikis, webhook, configid, rcgcdw.wiki, rcgcdw.lang, display, rcid, postid FROM discord LEFT JOIN rcgcdw ON discord.guild = rcgcdw.guild WHERE discord.guild = $1 AND discord.channel IS NULL ORDER BY configid ASC', [guild.id] ).then( ({rows}) => {
@@ -221,9 +223,15 @@ function dashboard_rcscript(res, $, guild, args, dashboardLang) {
 			return got.get( 'https://discord.com/api/webhooks/' + row.webhook ).then( response => {
 				if ( !response.body?.channel_id ) {
 					console.log( '- Dashboard: ' + response.statusCode + ': Error while getting the webhook: ' + response.body?.message );
-					row.channel = 'UNKNOWN';
-					row.name = 'UNKNOWN';
-					row.avatar = '';
+					if ( ( response.body?.message === 'Unknown Webhook' && response.body?.code === 10015 )
+					|| ( response.body?.message === 'Invalid Webhook Token' && response.body?.code === 50027 ) ) {
+						row.DELETED = true;
+					}
+					else {
+						row.channel = 'UNKNOWN';
+						row.name = 'UNKNOWN';
+						row.avatar = '';
+					}
 				}
 				else {
 					row.channel = response.body.channel_id;
@@ -237,6 +245,15 @@ function dashboard_rcscript(res, $, guild, args, dashboardLang) {
 				row.avatar = '';
 			} );
 		} )).finally( () => {
+			if ( rows.some( row => row.DELETED ) ) {
+				let deletedRows = rows.filter( row => row.DELETED ).map( row => row.webhook );
+				db.query( 'DELETE FROM rcgcdw WHERE webhook IN (' + deletedRows.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', deletedRows ).then( () => {
+					console.log( '- Dashboard: Deleted RcGcDw successfully removed.' );
+				}, dberror => {
+					console.log( '- Dashboard: Error while removing the deleted RcGcDw: ' + dberror );
+				} );
+				rows = rows.filter( row => !row.DELETED );
+			}
 			let suffix = ( args[0] === 'owner' ? '?owner=true' : '' );
 			$('#channellist #rcscript').after(
 				...rows.map( row => {
@@ -257,7 +274,7 @@ function dashboard_rcscript(res, $, guild, args, dashboardLang) {
 			if ( args[4] === 'new' && !( process.env.READONLY || rows.length >= rcgcdwLimit[( guild.patreon ? 'patreon' : 'default' )] ) ) {
 				$('.channel#channel-new').addClass('selected');
 				createForm($, dashboardLang.get('rcscript.form.new'), dashboardLang, {
-					wiki, lang: ( allLangs.names.hasOwnProperty(lang) ? lang : defaultSettings.lang ),
+					wiki, lang: ( allLangs.hasOwnProperty(lang) ? lang : defaultSettings.lang ),
 					display: 1, patreon: guild.patreon
 				}, guild.channels, allwikis).attr('action', `/guild/${guild.id}/rcscript/new`).appendTo('#text');
 			}
@@ -317,7 +334,7 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 		return res(`/guild/${guild}/rcscript/${type}`, 'savefail');
 	}
 	if ( settings.save_settings ) {
-		if ( !settings.wiki || !allLangs.names.hasOwnProperty(settings.lang) ) {
+		if ( !settings.wiki || !allLangs.hasOwnProperty(settings.lang) ) {
 			return res(`/guild/${guild}/rcscript/${type}`, 'savefail');
 		}
 		if ( !['0', '1', '2', '3'].includes( settings.display ) ) {
@@ -454,7 +471,7 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 								text += `\n${lang.get('rcscript.name')} \`${( settings.name || body.query.allmessages[1]['*'] || 'Recent changes' )}\``;
 								if ( settings.avatar ) text += `\n${lang.get('rcscript.avatar')} <${settings.avatar}>`;
 								text += `\n${lang.get('rcscript.wiki')} <${wiki.href}>`;
-								text += `\n${lang.get('rcscript.lang')} \`${allLangs.names[settings.lang]}\``;
+								text += `\n${lang.get('rcscript.lang')} \`${allLangs[settings.lang]}\``;
 								text += `\n${lang.get('rcscript.display')} \`${display_types[settings.display]}\``;
 								if ( enableFeeds && settings.feeds_only ) text += `\n${lang.get('rcscript.rc')} *\`${lang.get('rcscript.disabled')}\`*`;
 								if ( wiki.isFandom(false) ) text += `\n${lang.get('rcscript.feeds')} *\`${lang.get('rcscript.' + ( enableFeeds ? 'enabled' : 'disabled' ))}\`*`;
@@ -571,7 +588,7 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 						text += `\n${lang.get('rcscript.channel')} <#${row.channel}>`;
 						text += `\n${lang.get('rcscript.name')} \`${row.name}\``;
 						text += `\n${lang.get('rcscript.wiki')} <${row.wiki}>`;
-						text += `\n${lang.get('rcscript.lang')} \`${allLangs.names[row.lang]}\``;
+						text += `\n${lang.get('rcscript.lang')} \`${allLangs[row.lang]}\``;
 						text += `\n${lang.get('rcscript.display')} \`${display_types[row.display]}\``;
 						if ( row.rcid === -1 ) {
 							text += `\n${lang.get('rcscript.rc')} *\`${lang.get('rcscript.disabled')}\`*`;
@@ -726,8 +743,8 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 								}
 								if ( row.lang !== settings.lang ) {
 									file.push(`./RcGcDb/locale/widgets/${settings.lang}.png`);
-									diff.push(lang.get('rcscript.lang') + ` ~~\`${allLangs.names[row.lang]}\`~~ → \`${allLangs.names[settings.lang]}\``);
-									webhook_diff.push(webhook_lang.get('dashboard.lang', allLangs.names[settings.lang]));
+									diff.push(lang.get('rcscript.lang') + ` ~~\`${allLangs[row.lang]}\`~~ → \`${allLangs[settings.lang]}\``);
+									webhook_diff.push(webhook_lang.get('dashboard.lang', allLangs[settings.lang]));
 								}
 								if ( row.display !== settings.display ) {
 									diff.push(lang.get('rcscript.display') + ` ~~\`${display_types[row.display]}\`~~ → \`${display_types[settings.display]}\``);
@@ -849,7 +866,7 @@ function update_rcscript(res, userSettings, guild, type, settings) {
 	} );
 }
 
-module.exports = {
-	get: dashboard_rcscript,
-	post: update_rcscript
+export {
+	dashboard_rcscript as get,
+	update_rcscript as post
 };

+ 22 - 20
dashboard/settings.js

@@ -1,9 +1,11 @@
-const cheerio = require('cheerio');
+import cheerio from 'cheerio';
+import Lang from '../util/i18n.js';
+import Wiki from '../util/wiki.js';
+import { got, db, sendMsg, createNotice, hasPerm } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {defaultSettings} = require('../util/default.json');
-const Lang = require('../util/i18n.js');
-const allLangs = Lang.allLangs();
-const Wiki = require('../util/wiki.js');
-const {got, db, sendMsg, createNotice, hasPerm} = require('./util.js');
+const allLangs = Lang.allLangs().names;
 
 const fieldset = {
 	channel: '<label for="wb-settings-channel">Channel:</label>'
@@ -18,8 +20,8 @@ const fieldset = {
 	//+ '</fieldset>',
 	lang: '<label for="wb-settings-lang">Language:</label>'
 	+ '<select id="wb-settings-lang" name="lang" required autocomplete="language">'
-	+ Object.keys(allLangs.names).map( lang => {
-		return `<option id="wb-settings-lang-${lang}" value="${lang}">${allLangs.names[lang]}</option>`
+	+ Object.keys(allLangs).map( lang => {
+		return `<option id="wb-settings-lang-${lang}" value="${lang}">${allLangs[lang]}</option>`
 	} ).join('')
 	+ '</select>'
 	+ '<img id="wb-settings-lang-widget">',
@@ -38,9 +40,9 @@ const fieldset = {
 
 /**
  * Create a settings form
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {String} header - The form header
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  * @param {Object} settings - The current settings
  * @param {Boolean} settings.patreon
  * @param {String} settings.channel
@@ -165,10 +167,10 @@ function createForm($, header, dashboardLang, settings, guildRoles, guildChannel
 /**
  * Let a user change settings
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {import('./util.js').Guild} guild - The current guild
  * @param {String[]} args - The url parts
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  */
 function dashboard_settings(res, $, guild, args, dashboardLang) {
 	db.query( 'SELECT channel, wiki, lang, role, inline, prefix, patreon FROM discord WHERE guild = $1 ORDER BY channel DESC NULLS LAST', [guild.id] ).then( ({rows}) => {
@@ -277,7 +279,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 		return res(`/guild/${guild}/settings/${type}`, 'savefail');
 	}
 	if ( settings.save_settings ) {
-		if ( !settings.wiki || ( settings.lang && !allLangs.names.hasOwnProperty(settings.lang) ) ) {
+		if ( !settings.wiki || ( settings.lang && !allLangs.hasOwnProperty(settings.lang) ) ) {
 			return res(`/guild/${guild}/settings/${type}`, 'savefail');
 		}
 		if ( settings.channel && !userSettings.guilds.isMember.get(guild).channels.some( channel => {
@@ -326,7 +328,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 				var text = lang.get('settings.dashboard.removed', `<@${userSettings.user.id}>`, `<#${type}>`);
 				if ( channel.wiki !== row.wiki ) text += `\n${lang.get('settings.currentwiki')} <${channel.wiki}>`;
 				if ( response.patreon ) {
-					if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs.names[channel.lang]}\``;
+					if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs[channel.lang]}\``;
 					if ( channel.role !== row.role ) text += `\n${lang.get('settings.currentrole')} ` + ( channel.role ? `<@&${channel.role}>` : '@everyone' );
 					if ( channel.inline !== row.inline ) text += `\n${lang.get('settings.currentinline')} ${( channel.inline ? '~~' : '' )}\`[[${( lang.localNames.page || 'page' )}]]\`${( channel.inline ? '~~' : '' )}`;
 				}
@@ -424,7 +426,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 					res(`/guild/${guild}/settings`, 'save');
 					var text = lang.get('settings.dashboard.updated', `<@${userSettings.user.id}>`);
 					text += '\n' + lang.get('settings.currentwiki') + ` <${wiki.href}>`;
-					text += '\n' + lang.get('settings.currentlang') + ` \`${allLangs.names[settings.lang]}\``;
+					text += '\n' + lang.get('settings.currentlang') + ` \`${allLangs[settings.lang]}\``;
 					text += '\n' + lang.get('settings.currentrole') + ( settings.role ? ` <@&${settings.role}>` : ' @everyone' );
 					if ( response.patreon ) {
 						text += '\n' + lang.get('settings.currentprefix') + ` \`${settings.prefix.replace( /\\/g, '\\$&' )}\``;
@@ -452,7 +454,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 				if ( row.lang !== settings.lang ) {
 					updateChannel = true;
 					file.push(`./i18n/widgets/${settings.lang}.png`);
-					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs.names[row.lang]}\`~~ → \`${allLangs.names[settings.lang]}\``);
+					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs[row.lang]}\`~~ → \`${allLangs[settings.lang]}\``);
 				}
 				if ( response.patreon && row.prefix !== settings.prefix ) {
 					updateChannel = true;
@@ -526,7 +528,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 					var text = lang.get('settings.dashboard.removed', `<@${userSettings.user.id}>`, `<#${type}>`);
 					if ( channel.wiki !== row.wiki ) text += `\n${lang.get('settings.currentwiki')} <${channel.wiki}>`;
 					if ( response.patreon ) {
-						if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs.names[channel.lang]}\``;
+						if ( channel.lang !== row.lang ) text += `\n${lang.get('settings.currentlang')} \`${allLangs[channel.lang]}\``;
 						if ( channel.role !== row.role ) text += `\n${lang.get('settings.currentrole')} ` + ( channel.role ? `<@&${channel.role}>` : '@everyone' );
 						if ( channel.inline !== row.inline ) text += `\n${lang.get('settings.currentinline')} ${( channel.inline ? '~~' : '' )}\`[[${( lang.localNames.page || 'page' )}]]\`${( channel.inline ? '~~' : '' )}`;
 					}
@@ -552,7 +554,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 				}
 				if ( response.patreon && channel.lang !== settings.lang ) {
 					file.push(`./i18n/widgets/${settings.lang}.png`);
-					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs.names[channel.lang]}\`~~ → \`${allLangs.names[settings.lang]}\``);
+					diff.push(lang.get('settings.currentlang') + ` ~~\`${allLangs[channel.lang]}\`~~ → \`${allLangs[settings.lang]}\``);
 				}
 				if ( response.patreon && channel.role !== ( settings.role || null ) ) {
 					diff.push(lang.get('settings.currentrole') + ` ~~${( channel.role ? `<@&${channel.role}>` : '@everyone' )}~~ → ${( settings.role ? `<@&${settings.role}>` : '@everyone' )}`);
@@ -599,7 +601,7 @@ function update_settings(res, userSettings, guild, type, settings) {
 	} );
 }
 
-module.exports = {
-	get: dashboard_settings,
-	post: update_settings
+export {
+	dashboard_settings as get,
+	update_settings as post
 };

+ 18 - 12
dashboard/slash.js

@@ -1,5 +1,5 @@
-const Lang = require('../util/i18n.js');
-const {got, db, slashCommands, sendMsg, createNotice, hasPerm} = require('./util.js');
+import Lang from '../util/i18n.js';
+import { got, db, slashCommands, sendMsg, createNotice, hasPerm } from './util.js';
 
 const fieldset = {
 	role: '<label for="wb-settings-addrole">Role:</label>'
@@ -22,9 +22,9 @@ const fieldset = {
 
 /**
  * Create a settings form
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {slashCommands[0]} slashCommand - The slash command
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  * @param {Object[]} permissions - The current permissions
  * @param {String} permissions.id
  * @param {Number} permissions.type
@@ -94,10 +94,10 @@ function createForm($, slashCommand, dashboardLang, permissions, guildId, guildR
 /**
  * Let a user change slashs
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {import('./util.js').Guild} guild - The current guild
  * @param {String[]} args - The url parts
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  */
 function dashboard_slash(res, $, guild, args, dashboardLang) {
 	let suffix = ( args[0] === 'owner' ? '?owner=true' : '' );
@@ -115,7 +115,9 @@ function dashboard_slash(res, $, guild, args, dashboardLang) {
 			headers: {
 				Authorization: `Bot ${process.env.token}`
 			},
-			timeout: 10000
+			timeout: {
+				request: 10000
+			}
 		} ).then( response=> {
 			var permissions = [];
 			if ( response.statusCode !== 200 || !response.body ) {
@@ -214,7 +216,9 @@ function update_slash(res, userSettings, guild, type, settings) {
 			headers: {
 				Authorization: `Bot ${process.env.token}`
 			},
-			timeout: 10000
+			timeout: {
+				request: 10000
+			}
 		} ).then( response=> {
 			if ( response.statusCode !== 200 || !response.body ) {
 				if ( response.statusCode === 403 && response.body?.message === 'Missing Access' ) {
@@ -245,7 +249,9 @@ function update_slash(res, userSettings, guild, type, settings) {
 					Authorization: `Bot ${process.env.token}`
 				},
 				json: {permissions},
-				timeout: 10000
+				timeout: {
+					request: 10000
+				}
 			} ).then( response=> {
 				if ( response.statusCode !== 200 || !response.body ) {
 					console.log( '- Dashboard: ' + response.statusCode + ': Error while saving the slash command permissions: ' + response.body?.message );
@@ -307,7 +313,7 @@ function update_slash(res, userSettings, guild, type, settings) {
 	} );
 }
 
-module.exports = {
-	get: dashboard_slash,
-	post: update_slash
+export {
+	dashboard_slash as get,
+	update_slash as post
 };

+ 10 - 0
dashboard/src/discord.svg

@@ -0,0 +1,10 @@
+<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0)">
+<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#5865F2"/>
+</g>
+<defs>
+<clipPath id="clip0">
+<rect width="71" height="55" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 6 - 1
dashboard/src/index.css

@@ -64,6 +64,10 @@ a[alt]:hover:after {
 	height: 48px;
 	margin-top: -48px;
 }
+#navbar > #selector > img {
+	width: 32px;
+	padding-right: 5px;
+}
 #navbar a {
 	display: flex;
 	align-items: center;
@@ -494,7 +498,7 @@ li {
 #login-button img {
 	width: 60px;
 	height: 60px;
-	padding-right: 10px;
+	padding-right: 25px;
 }
 .server-selector {
 	display: flex;
@@ -587,6 +591,7 @@ fieldset textarea {
 #wb-settings-avatar-preview-img {
 	width: 128px;
 	height: 128px;
+	image-rendering: pixelated;
 	background: #32353b;
 }
 .theme-light #wb-settings-avatar-preview-img {

+ 34 - 2
dashboard/src/index.js

@@ -150,7 +150,7 @@ if ( wiki ) {
 			if ( regex ) wikinew = regex[1];
 			else if ( !wiki.validity.valid ) return wiki.reportValidity();
 			else {
-				wikinew = wikinew.replace( /\/(?:index|api|load|rest)\.php(?:|\?.*)$/, '' ).replace( /\/$/, '' );
+				wikinew = wikinew.replace( /\/(?:index|api|load|rest)\.php(?:|[\?\/#].*)$/, '' ).replace( /\/$/, '' );
 			}
 			var readonly = wiki.readOnly;
 			wiki.readOnly = true;
@@ -430,6 +430,38 @@ if ( usergroup ) {
 			multigroup.disabled = true;
 		}
 	} );
+
+	function fillUsergroupList({query: {allmessages: wikiUsergroupList = [], usergroups: wikiUsergroupListPermissions = []} = {}} = {}) {
+		if ( !wikiUsergroupList.length ) return;
+		usergrouplist.replaceChildren(...[
+			...wikiUsergroupList.filter( wikigroup => {
+				if ( wikigroup.name === 'group-all' ) return false;
+				if ( wikigroup.name === 'group-membership-link-with-expiry' ) return false;
+				if ( wikigroup.name.endsWith( '.css' ) || wikigroup.name.endsWith( '.js' ) ) return false;
+				if ( wikigroup.name.endsWith( '-member' ) && wikiUsergroupList.some( wikigroupmember => {
+					return wikigroupmember.name === wikigroup.name.replace( /-member$/, '' );
+				} ) ) return false;
+				return true;
+			} ).map( wikigroup => {
+				return new Option(wikigroup['*'], wikigroup.name.replace( /^group-/, '' ));
+			} ),
+			...wikiUsergroupListPermissions.map( wikigroup => wikigroup.name ).filter( function(wikigroup) {
+				if ( wikigroup === '*' ) return false;
+				if ( wikiUsergroupList.some( wikigroupmember => {
+					if ( 'group-' + wikigroup === wikigroupmember.name ) return true;
+					if ( 'group-' + wikigroup + '-member' === wikigroupmember.name ) return true;
+					return false;
+				} ) ) return false;
+				return true;
+			} ).map( wikigroup => {
+				return new Option(wikigroup, wikigroup);
+			} )
+		].sort( (a, b) => {
+			if ( a.value < b.value ) return -1;
+			if ( a.value > b.value ) return 1;
+			return 0;
+		} ));
+	}
 }
 
 /** @type {NodeListOf<HTMLInputElement>} */
@@ -600,4 +632,4 @@ for ( var i = 0; i < collapsible.length; i++ ) {
 		}
 	};
 }
-*/
+*/

+ 6 - 6
dashboard/user.js

@@ -1,11 +1,11 @@
-const {db, enabledOAuth2, oauthVerify} = require('./util.js');
+import { db, enabledOAuth2 } from './util.js';
 
 /**
  * Let a user change settings
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {import('./util.js').User} user - The current user
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  */
 function dashboard_user(res, $, user, dashboardLang) {
 	db.query( 'SELECT site, token FROM oauthusers WHERE userid = $1', [user.id] ).then( ({rows}) => {
@@ -123,7 +123,7 @@ function update_user(res, user_id, type, oauth_id) {
 	} );
 }
 
-module.exports = {
-	get: dashboard_user,
-	post: update_user
+export {
+	dashboard_user as get,
+	update_user as post
 };

+ 61 - 35
dashboard/util.js

@@ -1,25 +1,35 @@
-const got = require('got').extend( {
+import gotDefault from 'got';
+import pg from 'pg';
+import DiscordOauth2 from 'discord-oauth2';
+import { oauthSites } from '../util/wiki.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const slashCommands = require('../interactions/commands.json');
+
+globalThis.isDebug = ( process.argv[2] === 'debug' );
+
+const got = gotDefault.extend( {
 	throwHttpErrors: false,
-	timeout: 5000,
+	timeout: {
+		request: 5000
+	},
 	headers: {
 		'User-Agent': 'Wiki-Bot/' + ( isDebug ? 'testing' : process.env.npm_package_version ) + '/dashboard (Discord; ' + process.env.npm_package_name + ( process.env.invite ? '; ' + process.env.invite : '' ) + ')'
 	},
 	responseType: 'json'
 } );
-const {Pool} = require('pg');
-const db = new Pool();
+
+const db = new pg.Pool();
 db.on( 'error', dberror => {
 	console.log( '- Dashboard: Error while connecting to the database: ' + dberror );
 } );
-const DiscordOauth2 = require('discord-oauth2');
+
 const oauth = new DiscordOauth2( {
 	clientId: process.env.bot,
 	clientSecret: process.env.secret,
 	redirectUri: process.env.dashboard
 } );
 
-const {oauthSites} = require('../util/wiki.js');
-
 const enabledOAuth2 = [
 	...oauthSites.filter( oauthSite => {
 		let site = new URL(oauthSite);
@@ -48,13 +58,13 @@ if ( process.env.oauth_wikimedia && process.env.oauth_wikimedia_secret ) {
 	});
 }
 
-const slashCommands = require('../interactions/commands.json');
-
 got.get( `https://discord.com/api/v8/applications/${process.env.bot}/commands`, {
 	headers: {
 		Authorization: `Bot ${process.env.token}`
 	},
-	timeout: 10000
+	timeout: {
+		request: 10000
+	}
 } ).then( response=> {
 	if ( response.statusCode !== 200 || !response.body ) {
 		console.log( '- Dashboard: ' + response.statusCode + ': Error while getting the global slash commands: ' + response.body?.message );
@@ -157,7 +167,7 @@ process.on( 'message', message => {
 		else messages.get(message.id).resolve(message.data.response);
 		return messages.delete(message.id);
 	}
-	if ( message === 'toggleDebug' ) global.isDebug = !global.isDebug;
+	if ( message === 'toggleDebug' ) isDebug = !isDebug;
 	console.log( '- [Dashboard]: Message received!', message );
 } );
 
@@ -223,9 +233,9 @@ if ( process.env.botlist ) {
 
 /**
  * Add bot list widgets.
- * @param {import('cheerio')} $ - The cheerio static
- * @param {import('./i18n.js')} dashboardLang - The user language
- * @returns {import('cheerio')}
+ * @param {import('cheerio').default} $ - The cheerio static
+ * @param {import('./i18n.js').default} dashboardLang - The user language
+ * @returns {import('cheerio').default}
 */
 function addWidgets($, dashboardLang) {
 	if ( !botLists.length ) return;
@@ -238,11 +248,11 @@ function addWidgets($, dashboardLang) {
 
 /**
  * Create a red notice
- * @param {import('cheerio')} $ - The cheerio static
+ * @param {import('cheerio').default} $ - The cheerio static
  * @param {String} notice - The notice to create
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  * @param {String[]} [args] - The arguments for the notice
- * @returns {import('cheerio')}
+ * @returns {import('cheerio').default}
  */
 function createNotice($, notice, dashboardLang, args = []) {
 	if ( !notice ) return;
@@ -406,30 +416,32 @@ function escapeText(text) {
 }
 
 const permissions = {
-	ADMINISTRATOR: 1 << 3,
-	MANAGE_CHANNELS: 1 << 4,
-	MANAGE_GUILD: 1 << 5,
-	ADD_REACTIONS: 1 << 6,
-	VIEW_CHANNEL: 1 << 10,
-	SEND_MESSAGES: 1 << 11,
-	MANAGE_MESSAGES: 1 << 13,
-	EMBED_LINKS: 1 << 14,
-	ATTACH_FILES: 1 << 15,
-	READ_MESSAGE_HISTORY: 1 << 16,
-	MENTION_EVERYONE: 1 << 17,
-	USE_EXTERNAL_EMOJIS: 1 << 18,
-	MANAGE_NICKNAMES: 1 << 27,
-	MANAGE_ROLES: 1 << 28,
-	MANAGE_WEBHOOKS: 1 << 29
+	ADMINISTRATOR: 1n << 3n,
+	MANAGE_CHANNELS: 1n << 4n,
+	MANAGE_GUILD: 1n << 5n,
+	ADD_REACTIONS: 1n << 6n,
+	VIEW_CHANNEL: 1n << 10n,
+	SEND_MESSAGES: 1n << 11n,
+	MANAGE_MESSAGES: 1n << 13n,
+	EMBED_LINKS: 1n << 14n,
+	ATTACH_FILES: 1n << 15n,
+	READ_MESSAGE_HISTORY: 1n << 16n,
+	MENTION_EVERYONE: 1n << 17n,
+	USE_EXTERNAL_EMOJIS: 1n << 18n,
+	MANAGE_NICKNAMES: 1n << 27n,
+	MANAGE_ROLES: 1n << 28n,
+	MANAGE_WEBHOOKS: 1n << 29n,
+	SEND_MESSAGES_IN_THREADS: 1n << 38n
 }
 
 /**
  * Check if a permission is included in the BitField
- * @param {String|Number} all - BitField of multiple permissions
+ * @param {String|Number|BigInt} all - BitField of multiple permissions
  * @param {String[]} permission - Name of the permission to check for
  * @returns {Boolean}
  */
-function hasPerm(all = 0, ...permission) {
+function hasPerm(all = 0n, ...permission) {
+	all = BigInt(all);
 	if ( (all & permissions.ADMINISTRATOR) === permissions.ADMINISTRATOR ) return true;
 	return permission.every( perm => {
 		let bit = permissions[perm];
@@ -437,4 +449,18 @@ function hasPerm(all = 0, ...permission) {
 	} );
 }
 
-module.exports = {got, db, oauth, enabledOAuth2, slashCommands, sessionData, settingsData, oauthVerify, sendMsg, addWidgets, createNotice, escapeText, hasPerm};
+export {
+	got,
+	db,
+	oauth,
+	enabledOAuth2,
+	slashCommands,
+	sessionData,
+	settingsData,
+	oauthVerify,
+	sendMsg,
+	addWidgets,
+	createNotice,
+	escapeText,
+	hasPerm
+};

+ 23 - 15
dashboard/verification.js

@@ -1,6 +1,8 @@
+import Lang from '../util/i18n.js';
+import { got, db, slashCommands, sendMsg, createNotice, escapeText, hasPerm } from './util.js';
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
 const {limit: {verification: verificationLimit}, usergroups} = require('../util/default.json');
-const Lang = require('../util/i18n.js');
-const {got, db, slashCommands, sendMsg, createNotice, escapeText, hasPerm} = require('./util.js');
 const slashCommand = slashCommands.find( slashCommand => slashCommand.name === 'verify' );
 
 const fieldset = {
@@ -58,9 +60,9 @@ const fieldset = {
 
 /**
  * Create a settings form
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {String} header - The form header
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  * @param {Object} settings - The current settings
  * @param {String} settings.channel
  * @param {String} settings.role
@@ -72,8 +74,9 @@ const fieldset = {
  * @param {String} [settings.defaultrole]
  * @param {import('./util.js').Channel[]} guildChannels - The guild channels
  * @param {import('./util.js').Role[]} guildRoles - The guild roles
+ * @param {String} wiki - The guild wiki
  */
-function createForm($, header, dashboardLang, settings, guildChannels, guildRoles) {
+function createForm($, header, dashboardLang, settings, guildChannels, guildRoles, wiki) {
 	var readonly = ( process.env.READONLY ? true : false );
 	var fields = [];
 	let channel = $('<div>').append(fieldset.channel);
@@ -194,6 +197,7 @@ function createForm($, header, dashboardLang, settings, guildChannels, guildRole
 		usergroup.find('#wb-settings-usergroup-multiple').attr('style', 'display: none;');
 	}
 	fields.push(usergroup);
+	$('<script>').attr('src', wiki + 'api.php?action=query&meta=allmessages|siteinfo&amprefix=group-&amincludelocal=true&amenableparser=true&amlang=' + dashboardLang.lang + '&siprop=usergroups&format=json&callback=fillUsergroupList').attr('defer', '').insertAfter('script#indexjs');
 	let editcount = $('<div>').append(fieldset.editcount);
 	editcount.find('label').text(dashboardLang.get('verification.form.editcount'));
 	editcount.find('#wb-settings-editcount').val(settings.editcount);
@@ -244,10 +248,10 @@ function createForm($, header, dashboardLang, settings, guildChannels, guildRole
 /**
  * Let a user change verifications
  * @param {import('http').ServerResponse} res - The server response
- * @param {import('cheerio')} $ - The response body
+ * @param {import('cheerio').default} $ - The response body
  * @param {import('./util.js').Guild} guild - The current guild
  * @param {String[]} args - The url parts
- * @param {import('./i18n.js')} dashboardLang - The user language
+ * @param {import('./i18n.js').default} dashboardLang - The user language
  */
 function dashboard_verification(res, $, guild, args, dashboardLang) {
 	db.query( 'SELECT wiki, discord.role defaultrole, prefix, configid, verification.channel, verification.role, editcount, postcount, usergroup, accountage, rename FROM discord LEFT JOIN verification ON discord.guild = verification.guild WHERE discord.guild = $1 AND discord.channel IS NULL ORDER BY configid ASC', [guild.id] ).then( ({rows}) => {
@@ -306,12 +310,12 @@ function dashboard_verification(res, $, guild, args, dashboardLang) {
 				channel: '', role: '', usergroup: 'user',
 				editcount: 0, postcount: 0, accountage: 0,
 				rename: false, defaultrole
-			}, guild.channels, guild.roles).attr('action', `/guild/${guild.id}/verification/new`).appendTo('#text');
+			}, guild.channels, guild.roles, wiki).attr('action', `/guild/${guild.id}/verification/new`).appendTo('#text');
 		}
 		else if ( rows.some( row => row.configid.toString() === args[4] ) ) {
 			let row = rows.find( row => row.configid.toString() === args[4] );
 			$(`.channel#channel-${row.configid}`).addClass('selected');
-			createForm($, dashboardLang.get('verification.form.entry', false, row.configid), dashboardLang, row, guild.channels, guild.roles).attr('action', `/guild/${guild.id}/verification/${row.configid}`).appendTo('#text');
+			createForm($, dashboardLang.get('verification.form.entry', false, row.configid), dashboardLang, row, guild.channels, guild.roles, wiki).attr('action', `/guild/${guild.id}/verification/${row.configid}`).appendTo('#text');
 		}
 		else if ( args[4] === 'notice' && rows.length ) {
 			$(`.channel#channel-notice`).addClass('selected');
@@ -525,7 +529,9 @@ function update_verification(res, userSettings, guild, type, settings) {
 					json: {
 						permissions: []
 					},
-					timeout: 10000
+					timeout: {
+						request: 10000
+					}
 				} ).then( response=> {
 					if ( response.statusCode !== 200 || !response.body ) {
 						console.log( '- Dashboard: ' + response.statusCode + ': Error while disabling the slash command: ' + response.body?.message );
@@ -633,7 +639,9 @@ function update_verification(res, userSettings, guild, type, settings) {
 								}
 							]
 						},
-						timeout: 10000
+						timeout: {
+							request: 10000
+						}
 					} ).then( response=> {
 						if ( response.statusCode !== 200 || !response.body ) {
 							console.log( '- Dashboard: ' + response.statusCode + ': Error while enabling the slash command: ' + response.body?.message );
@@ -1002,7 +1010,7 @@ function update_notices(res, userSettings, guild, type, settings) {
 	} );
 }
 
-module.exports = {
-	get: dashboard_verification,
-	post: update_verification
-};
+export {
+	dashboard_verification as get,
+	update_verification as post
+};

+ 15 - 13
database.js

@@ -1,6 +1,8 @@
+import { createRequire } from 'module';
+import pg from 'pg';
+const require = createRequire(import.meta.url);
 const {defaultSettings} = require('./util/default.json');
-const {Client} = require('pg');
-const db = new Client();
+const db = new pg.Client();
 db.on( 'error', dberror => {
 	console.log( '- Error while connecting to the database: ' + dberror );
 } );
@@ -206,8 +208,16 @@ COMMIT TRANSACTION;
 ALTER DATABASE "${process.env.PGDATABASE}" SET my.version TO 4;
 `];
 
-module.exports = db.connect().then( () => {
-	return db.query( 'SELECT CURRENT_SETTING($1) AS version', ['my.version'] ).then( ({rows:[row]}) => {
+export default await db.connect().then( () => {
+	return db.query( 'SELECT CURRENT_SETTING($1, $2) AS version', ['my.version', true] ).then( ({rows:[row]}) => {
+		if ( row.version === null ) {
+			return db.query( schema[0] ).then( () => {
+				console.log( '- The database has been updated to: v' + schema.length );
+			}, dberror => {
+				console.log( '- Error while updating the database: ' + dberror );
+				return Promise.reject();
+			} );
+		}
 		row.version = parseInt(row.version, 10);
 		if ( isNaN(row.version) || row.version > schema.length ) {
 			console.log( '- Invalid database version: v' + row.version );
@@ -229,14 +239,6 @@ module.exports = db.connect().then( () => {
 			return Promise.reject();
 		} );
 	}, dberror => {
-		if ( dberror.message === 'unrecognized configuration parameter "my.version"' ) {
-			return db.query( schema[0] ).then( () => {
-				console.log( '- The database has been updated to: v' + schema.length );
-			}, dberror => {
-				console.log( '- Error while updating the database: ' + dberror );
-				return Promise.reject();
-			} );
-		}
 		console.log( '- Error while getting the database version: ' + dberror );
 		return Promise.reject();
 	} );
@@ -253,6 +255,6 @@ module.exports = db.connect().then( () => {
 	}, dberror => {
 		console.log( '- Error while closing the database connection: ' + dberror );
 	} ).then( () => {
-		return Promise.reject();
+		process.exit(1);
 	} );
 } );

+ 7 - 9
functions/global_block.js

@@ -1,19 +1,19 @@
-const cheerio = require('cheerio');
-const {got, escapeFormatting} = require('../util/functions.js');
+import cheerio from 'cheerio';
+import { got, escapeFormatting } from '../util/functions.js';
 
 /**
  * Add global blocks to user messages.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} username - The name of the user.
  * @param {String} text - The text of the response.
  * @param {import('discord.js').MessageEmbed} embed - The embed for the page.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the page.
  * @param {String} spoiler - If the response is in a spoiler.
  * @param {String} [gender] - The gender of the user.
  */
-function global_block(lang, msg, username, text, embed, wiki, spoiler, gender) {
-	if ( !msg || !msg.channel.isGuild() || !patreons[msg.guildId] || !wiki.isFandom() ) return;
+export default function global_block(lang, msg, username, text, embed, wiki, spoiler, gender) {
+	if ( !msg || !msg.inGuild() || !patreonGuildsPrefix.has(msg.guildId) || !wiki.isFandom() ) return;
 	
 	var isUser = true;
 	if ( !gender ) {
@@ -100,6 +100,4 @@ function global_block(lang, msg, username, text, embed, wiki, spoiler, gender) {
 	]).finally( () => {
 		msg.edit( {content: spoiler + text + spoiler, embeds: [embed]} ).catch(log_error);
 	} );
-}
-
-module.exports = global_block;
+}

+ 4 - 6
functions/helpserver.js

@@ -1,13 +1,11 @@
-const help_setup = require('./helpsetup.js');
+import help_setup from './helpsetup.js';
 
 /**
  * Post a message about the help server.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  */
-function help_server(lang, msg) {
+export default function help_server(lang, msg) {
 	if ( msg.isAdmin() && msg.defaultSettings ) help_setup(lang, msg);
 	msg.sendChannel( lang.get('general.helpserver') + '\n' + process.env.invite );
-}
-
-module.exports = help_server;
+}

+ 3 - 5
functions/helpsetup.js

@@ -1,11 +1,9 @@
 /**
  * Send send message to setup the bot.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  */
-function help_setup(lang, msg) {
+export default function help_setup(lang, msg) {
 	msg.defaultSettings = false;
 	msg.replyMsg( lang.get('general.default', '`' + process.env.prefix + 'settings`') + ( process.env.dashboard ? '\n' + new URL(`/guild/${msg.guildId}/settings`, process.env.dashboard).href : '' ) );
-}
-
-module.exports = help_setup;
+}

+ 122 - 15
functions/parse_page.js

@@ -1,7 +1,7 @@
-const cheerio = require('cheerio');
-const {MessageEmbed} = require('discord.js');
-const {toSection} = require('../util/wiki.js');
-const {got, parse_infobox, htmlToPlain, htmlToDiscord, escapeFormatting, limitLength} = require('../util/functions.js');
+import cheerio from 'cheerio';
+import { MessageEmbed } from 'discord.js';
+import { toSection } from '../util/wiki.js';
+import { got, parse_infobox, htmlToPlain, htmlToDiscord, escapeFormatting, limitLength } from '../util/functions.js';
 
 const parsedContentModels = [
 	'wikitext',
@@ -51,6 +51,7 @@ const removeClasses = [
 	'.noprint',
 	'.noexcerpt',
 	'.sortkey',
+	'.mw-collapsible.mw-collapsed',
 	'wb\\:sectionedit'
 ];
 
@@ -59,8 +60,10 @@ const removeClassesExceptions = [
 	'div.lcs-container',
 	'div.mw-highlight',
 	'div.poem',
+	'div.hlist',
 	'div.treeview',
 	'div.redirectMsg',
+	'div.introduction',
 	'div.wikibase-entityview',
 	'div.wikibase-entityview-main',
 	'div.wikibase-entitytermsview',
@@ -73,16 +76,19 @@ const removeClassesExceptions = [
 
 /**
  * Parses a wiki page to get it's description.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
  * @param {String} content - The content for the message.
  * @param {import('discord.js').MessageEmbed} embed - The embed for the message.
- * @param {import('../util/wiki.js')} wiki - The wiki for the page.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the page.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {Object} querypage - The details of the page.
+ * @param {Number} querypage.ns - The namespace of the page.
  * @param {String} querypage.title - The title of the page.
  * @param {String} querypage.contentmodel - The content model of the page.
+ * @param {String} querypage.pagelanguage - The language of the page.
  * @param {String} [querypage.missing] - If the page doesn't exist.
+ * @param {String} [querypage.known] - If the page is known.
  * @param {Object} [querypage.pageprops] - The properties of the page.
  * @param {String} [querypage.pageprops.infoboxes] - The JSON data for portable infoboxes on the page.
  * @param {String} [querypage.pageprops.disambiguation] - The disambiguation property of the page.
@@ -93,9 +99,9 @@ const removeClassesExceptions = [
  * @param {String} [pagelink] - The link to the page.
  * @returns {Promise<import('discord.js').Message>} The edited message.
  */
-function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmodel, missing, pageprops: {infoboxes, disambiguation} = {}, uselang = lang.lang, noRedirect = false}, thumbnail = '', fragment = '', pagelink = '') {
+export default function parse_page(lang, msg, content, embed, wiki, reaction, {ns, title, contentmodel, pagelanguage, missing, known, pageprops: {infoboxes, disambiguation} = {}, uselang = lang.lang, noRedirect = false}, thumbnail = '', fragment = '', pagelink = '') {
 	if ( reaction ) reaction.removeEmoji();
-	if ( !msg?.showEmbed?.() || missing !== undefined || !embed || embed.description ) {
+	if ( !msg?.showEmbed?.() || ( missing !== undefined && ( ns !== 8 || known === undefined ) ) || !embed || embed.description ) {
 		if ( missing !== undefined && embed ) {
 			if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
 				embed.spliceFields( 0, 0, embed.backupField );
@@ -111,11 +117,81 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 		embeds: [new MessageEmbed(embed).setDescription( '<a:loading:641343250661113886> **' + lang.get('search.loading') + '**' )]
 	} ).then( message => {
 		if ( !message ) return;
+		if ( ns === 8 ) {
+			title = title.split(':').slice(1).join(':');
+			if ( title.endsWith( '/' + pagelanguage ) ) title = title.substring(0, title.length - ( pagelanguage.length + 1 ));
+			return got.get( wiki + 'api.php?action=query&meta=allmessages&amprop=default&amincludelocal=true&amlang=' + encodeURIComponent( pagelanguage ) + '&ammessages=' + encodeURIComponent( title ) + '&format=json', {
+				timeout: {
+					request: 10_000
+				}
+			} ).then( response => {
+				var body = response.body;
+				if ( body && body.warnings ) log_warning(body.warnings);
+				if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !body.query?.allmessages?.[0] ) {
+					console.log( '- ' + response.statusCode + ': Error while getting the system message: ' + body?.error?.info );
+					if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+						embed.spliceFields( 0, 0, embed.backupField );
+					}
+					if ( embed.backupDescription && embed.length < 5000 ) {
+						embed.setDescription( embed.backupDescription );
+					}
+					return;
+				}
+				if ( !embed.description && embed.length < 4000 ) {
+					var description = body.query.allmessages[0]['*'];
+					var regex = /^L(\d+)(?:-L?(\d+))?$/.exec(fragment);
+					if ( regex ) {
+						let descArray = description.split('\n').slice(regex[1] - 1, ( regex[2] || regex[1] ));
+						if ( descArray.length ) {
+							description = descArray.join('\n').replace( /^\n+/, '' ).replace( /\n+$/, '' );
+							if ( description ) {
+								if ( description.length > 2000 ) description = description.substring(0, 2000) + '\u2026';
+								description = '```' + ( contentModels[contentmodel] || '' ) + '\n' + description + '\n```';
+								embed.setDescription( description );
+							}
+						}
+					}
+					else {
+						let defaultDescription = body.query.allmessages[0].default;
+						if ( description.trim() ) {
+							description = description.replace( /^\n+/, '' ).replace( /\n+$/, '' );
+							if ( description.length > 500 ) description = description.substring(0, 500) + '\u2026';
+							description = '```' + ( contentModels[contentmodel] || '' ) + '\n' + description + '\n```';
+							embed.setDescription( description );
+						}
+						else if ( embed.backupDescription ) {
+							embed.setDescription( embed.backupDescription );
+						}
+						if ( defaultDescription?.trim() ) {
+							defaultDescription = defaultDescription.replace( /^\n+/, '' ).replace( /\n+$/, '' );
+							if ( defaultDescription.length > 250 ) defaultDescription = defaultDescription.substring(0, 250) + '\u2026';
+							defaultDescription = '```' + ( contentModels[contentmodel] || '' ) + '\n' + defaultDescription + '\n```';
+							embed.addField( lang.get('search.messagedefault'), defaultDescription );
+						}
+						else if ( body.query.allmessages[0].defaultmissing !== undefined ) {
+							embed.addField( lang.get('search.messagedefault'), lang.get('search.messagedefaultnone') );
+						}
+					}
+				}
+			}, error => {
+				console.log( '- Error while getting the system message: ' + error );
+				if ( embed.backupField && embed.length < 4750 && embed.fields.length < 25 ) {
+					embed.spliceFields( 0, 0, embed.backupField );
+				}
+				if ( embed.backupDescription && embed.length < 5000 ) {
+					embed.setDescription( embed.backupDescription );
+				}
+			} ).then( () => {
+				return message.edit( {content, embeds: [embed]} ).catch(log_error);
+			} );
+		}
 		if ( !parsedContentModels.includes( contentmodel ) ) return got.get( wiki + 'api.php?action=query&prop=revisions&rvprop=content&rvslots=main&converttitles=true&titles=%1F' + encodeURIComponent( title ) + '&format=json', {
-			timeout: 10000
+			timeout: {
+				request: 10_000
+			}
 		} ).then( response => {
 			var body = response.body;
-			if ( body && body.warnings ) log_warn(body.warnings);
+			if ( body && body.warnings ) log_warning(body.warnings);
 			var revision = Object.values(( body?.query?.pages || {} ))?.[0]?.revisions?.[0];
 			revision = ( revision?.slots?.main || revision );
 			if ( response.statusCode !== 200 || !body || body.batchcomplete === undefined || !revision?.['*'] ) {
@@ -172,8 +248,11 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 				console.log( '- Failed to parse the infobox: ' + error );
 			}
 		}
+		let extraImages = [];
 		return got.get( wiki + 'api.php?uselang=' + uselang + '&action=parse' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=text|images|displaytitle' + ( contentmodel !== 'wikitext' || fragment || disambiguation !== undefined ? '' : '&section=0' ) + '&disablelimitreport=true&disableeditsection=true&disabletoc=true&sectionpreview=true&page=' + encodeURIComponent( title ) + '&format=json', {
-			timeout: 10000
+			timeout: {
+				request: 10_000
+			}
 		} ).then( response => {
 			if ( response.statusCode !== 200 || !response?.body?.parse?.text ) {
 				console.log( '- ' + response.statusCode + ': Error while parsing the page: ' + response?.body?.error?.info );
@@ -343,6 +422,28 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 						section.nextUntil(['h1','h2','h3','h4','h5','h6'].slice(0, sectionLevel).join(', '))
 					);
 					section.find('div, ' + removeClasses.join(', ')).remove();
+					extraImages.push(...new Set([
+						...sectionContent.find(infoboxList.join(', ')).find([
+							'tr:eq(1) img',
+							'div.images img',
+							'figure.pi-image img',
+							'div.infobox-imagearea img'
+						].join(', ')).toArray(),
+						...sectionContent.find([
+							'ul.gallery > li.gallerybox img',
+							'div.wikia-gallery > div.wikia-gallery-item img',
+							'div.ogv-gallery > div.ogv-gallery-item img'
+						].join(', ')).toArray()
+					].filter( img => {
+						let imgURL = ( img.attribs.src?.startsWith?.( 'data:' ) ? img.attribs['data-src'] : img.attribs.src );
+						if ( !imgURL ) return false;
+						return ( /^(?:https?:)?\/\//.test(imgURL) && /\.(?:png|jpg|jpeg|gif)(?:\/|\?|$)/i.test(imgURL) );
+					} ).map( img => {
+						if ( img.attribs['data-image-name']?.endsWith( '.gif' ) ) return wiki.toLink('Special:FilePath/' + img.attribs['data-image-name']);
+						let imgURL = ( img.attribs.src?.startsWith?.( 'data:' ) ? img.attribs['data-src'] : img.attribs.src );
+						imgURL = imgURL.replace( /\/thumb(\/[\da-f]\/[\da-f]{2}\/([^\/]+))\/\d+px-\2/, '$1' ).replace( /\/scale-to-width-down\/\d+/, '' );
+						return new URL(imgURL.replace( /^(?:https?:)?\/\//, 'https://' ), wiki).href;
+					} )));
 					sectionContent.find(infoboxList.join(', ')).remove();
 					sectionContent.find('div, ' + removeClasses.join(', ')).not(removeClassesExceptions.join(', ')).remove();
 					var name = htmlToPlain(section).trim();
@@ -394,9 +495,15 @@ function parse_page(lang, msg, content, embed, wiki, reaction, {title, contentmo
 				embed.spliceFields( 0, 0, embed.backupField );
 			}
 		} ).then( () => {
-			return message.edit( {content, embeds: [embed]} ).catch(log_error);
+			let embeds = [embed];
+			if ( extraImages.length ) {
+				if ( !embed.image ) embed.setImage( extraImages.shift() );
+				extraImages.slice(0, 10).forEach( extraImage => {
+					let imageEmbed = new MessageEmbed().setURL( embed.url ).setImage( extraImage );
+					if ( embeds.length < 5 && embeds.reduce( (acc, val) => acc + val.length, imageEmbed.length ) <= 5500 ) embeds.push(imageEmbed);
+				} );
+			}
+			return message.edit( {content, embeds} ).catch(log_error);
 		} );
 	} );
-}
-
-module.exports = parse_page;
+}

+ 8 - 10
functions/phabricator.js

@@ -1,18 +1,18 @@
-const {MessageEmbed} = require('discord.js');
-const logging = require('../util/logging.js');
-const {got, escapeFormatting, limitLength} = require('../util/functions.js');
+import { MessageEmbed } from 'discord.js';
+import logging from '../util/logging.js';
+import { got, escapeFormatting, limitLength } from '../util/functions.js';
 
 /**
  * Sends a Phabricator task.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').Message} msg - The Discord message.
- * @param {import('../util/wiki.js')} wiki - The wiki.
+ * @param {import('../util/wiki.js').default} wiki - The wiki.
  * @param {URL} link - The link.
  * @param {import('discord.js').MessageReaction} reaction - The reaction on the message.
  * @param {String} [spoiler] - If the response is in a spoiler.
  * @param {Boolean} [noEmbed] - If the response should be without an embed.
  */
-function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '', noEmbed = false) {
+export default function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '', noEmbed = false) {
 	var regex = /^(?:https?:)?\/\/phabricator\.(wikimedia|miraheze)\.org\/T(\d+)(?:#|$)/.exec(link.href);
 	if ( !regex || !process.env['phabricator_' + regex[1]] ) {
 		logging(wiki, msg.guildId, 'interwiki');
@@ -47,7 +47,7 @@ function phabricator_task(lang, msg, wiki, link, reaction, spoiler = '', noEmbed
 		}
 		var summary = escapeFormatting(task.fields.name);
 		if ( summary.length > 250 ) summary = summary.substring(0, 250) + '\u2026';
-		var embed = new MessageEmbed().setAuthor( 'Phabricator' ).setTitle( summary ).setURL( link ).addField( 'Status', escapeFormatting(task.fields.status.name), true ).addField( 'Priority', escapeFormatting(task.fields.priority.name), true );
+		var embed = new MessageEmbed().setAuthor( {name: 'Phabricator'} ).setTitle( summary ).setURL( link ).addField( 'Status', escapeFormatting(task.fields.status.name), true ).addField( 'Priority', escapeFormatting(task.fields.priority.name), true );
 		if ( task.fields.subtype !== 'default' ) embed.addField( 'Subtype', escapeFormatting(task.fields.subtype), true );
 		var description = parse_text( task.fields.description.raw, site );
 		if ( description.length > 2000 ) description = limitLength(description, 2000, 40);
@@ -123,6 +123,4 @@ function parse_text(text, site) {
 	text = text.replace( /(?<!https?:\/\/[^\s]+)\b\{?([CDFHLMPQTV]\d+(?:#\d+)?)\}?\b/g, '[$1](' + site + '$1)' );
 	text = text.replace( /(?<!https?:\/\/[^\s]+)#([a-z0-9_-]+)\b/g, '[#$1](' + site + 'tag/$1)' );
 	return text;
-}
-
-module.exports = phabricator_task;
+}

+ 55 - 56
functions/verify.js

@@ -1,24 +1,23 @@
-const cheerio = require('cheerio');
-const {MessageEmbed, MessageActionRow, MessageButton, Permissions: {FLAGS}} = require('discord.js');
-var db = require('../util/database.js');
-const Lang = require('../util/i18n.js');
-const Wiki = require('../util/wiki.js');
-const logging = require('../util/logging.js');
-const {got, oauthVerify, allowDelete, escapeFormatting} = require('../util/functions.js');
-const toTitle = require('../util/wiki.js').toTitle;
+import cheerio from 'cheerio';
+import { MessageEmbed, MessageActionRow, MessageButton, Permissions } from 'discord.js';
+import db from '../util/database.js';
+import Lang from '../util/i18n.js';
+import Wiki from '../util/wiki.js';
+import logging from '../util/logging.js';
+import { got, oauthVerify, allowDelete, escapeFormatting } from '../util/functions.js';
 
 /**
  * Processes the "verify" command.
- * @param {import('../util/i18n.js')} lang - The user language.
+ * @param {import('../util/i18n.js').default} lang - The user language.
  * @param {import('discord.js').TextChannel} channel - The Discord channel.
  * @param {import('discord.js').GuildMember} member - The Discord guild member.
  * @param {String} username - The username.
- * @param {import('../util/wiki.js')} wiki - The wiki for the message.
+ * @param {import('../util/wiki.js').default} wiki - The wiki for the message.
  * @param {Object[]} rows - The verification settings.
  * @param {String} [old_username] - The username before the search.
  * @returns {Promise<{content:String,embed:MessageEmbed,add_button:Boolean,send_private:Boolean,reaction:String,oauth:String[],logging:{channel:String,content:String,embed?:MessageEmbed}}>}
  */
-function verify(lang, channel, member, username, wiki, rows, old_username = '') {
+export default function verify(lang, channel, member, username, wiki, rows, old_username = '') {
 	/** @type {{logchannel:import('discord.js').TextChannel,flags:Number,onsuccess:String,onmatch:String}} */
 	var verifynotice = {
 		logchannel: rows[0].logchannel,
@@ -27,12 +26,12 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 		onmatch: rows[0].onmatch
 	};
 	verifynotice.logchannel = ( verifynotice.logchannel ? channel.guild.channels.cache.filter( logchannel => {
-		return ( logchannel.isGuild() && logchannel.permissionsFor(channel.guild.me).has([FLAGS.VIEW_CHANNEL, FLAGS.SEND_MESSAGES]) );
+		return ( logchannel.isText() && logchannel.permissionsFor(channel.guild.me).has([Permissions.FLAGS.VIEW_CHANNEL, Permissions.FLAGS.SEND_MESSAGES]) );
 	} ).get(verifynotice.logchannel) : null );
-	var embed = new MessageEmbed().setFooter( lang.get('verify.footer') ).setTimestamp();
+	var embed = new MessageEmbed().setFooter( {text: lang.get('verify.footer')} ).setTimestamp();
 	var result = {
 		content: '', embed,
-		add_button: channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS),
+		add_button: channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS),
 		send_private: ( (verifynotice.flags & 1 << 0) === 1 << 0 ),
 		reaction: '', oauth: [],
 		logging: {
@@ -43,7 +42,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 	};
 	return got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=users' + ( wiki.isFandom() ? '|usercontribs&ucprop=&uclimit=10&ucuser=' + encodeURIComponent( username ) : '' ) + '&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
 		var body = response.body;
-		if ( body && body.warnings ) log_warn(body.warnings);
+		if ( body && body.warnings ) log_warning(body.warnings);
 		if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.users ) {
 			if ( wiki.noWiki(response.url, response.statusCode) ) {
 				console.log( '- This wiki doesn\'t exist!' );
@@ -75,7 +74,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 		}
 		if ( !old_username ) logging(wiki, channel.guildId, 'verification');
 		var queryuser = body.query.users[0];
-		embed.setAuthor( body.query.general.sitename );
+		embed.setAuthor( {name: body.query.general.sitename} );
 		if ( body.query.users.length !== 1 || queryuser.missing !== undefined || queryuser.invalid !== undefined ) {
 			username = ( body.query.users.length === 1 ? queryuser.name : username );
 			embed.setTitle( escapeFormatting( old_username || username ) ).setColor('#0000FF').setDescription( lang.get('verify.user_missing', escapeFormatting( old_username || username )) ).addField( lang.get('verify.notice'), lang.get('verify.help_missing') );
@@ -101,7 +100,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 			result.content = lang.get('verify.user_blocked_reply', escapeFormatting(username), queryuser.gender);
 			if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 				result.logging.channel = verifynotice.logchannel.id;
-				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 					let logembed = new MessageEmbed(embed);
 					logembed.addField( lang.get('verify.discord', 'unknown'), escapeFormatting(member.user.tag) + ` (${member.toString()})`, true );
 					result.logging.embed = logembed;
@@ -179,7 +178,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 					embed.setColor('#FFFF00').setDescription( lang.get('verify.user_failed', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
 					if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 						result.logging.channel = verifynotice.logchannel.id;
-						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 							result.logging.embed = new MessageEmbed(embed);
 						}
 						else {
@@ -188,9 +187,10 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 							result.logging.content = logtext;
 						}
 					}
+					let prefix = ( patreonGuildsPrefix.get(channel.guildId) ?? process.env.prefix );
 					var help_link = '';
-					if ( wiki.isGamepedia() ) help_link = lang.get('verify.help_gamepedia') + '?c=' + ( patreons[channel.guildId] && patreons[channel.guildId] !== process.env.prefix ? encodeURIComponent( patreons[channel.guildId] + '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.guildId] && patreons[channel.guildId] !== process.env.prefix ? encodeURIComponent( patreons[channel.guildId] + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator + '&useskin=fandomdesktop';
+					if ( wiki.isGamepedia() ) help_link = lang.get('verify.help_gamepedia') + '?c=' + ( prefix !== '!wiki ' ? encodeURIComponent( prefix + 'verify' ) : 'wb' ) + ( channel.name !== 'verification' ? '&ch=' + encodeURIComponent( channel.name ) : '' ) + '&user=' + Wiki.toTitle(username) + '&discord=' + encodeURIComponent( member.user.username ) + '&tag=' + member.user.discriminator + '&useskin=fandomdesktop';
+					else if ( wiki.isFandom() ) help_link = lang.get('verify.help_fandom') + '/' + Wiki.toTitle(username) + '?c=' + ( prefix !== '!wiki ' ? encodeURIComponent( prefix + '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 );
 					result.content = lang.get('verify.user_failed_reply', escapeFormatting(username), queryuser.gender);
 					return;
@@ -206,7 +206,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 				var removeRoles = [new Set(), new Set()];
 				var verified = false;
 				var rename = false;
-				var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86400000;
+				var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86_400_000;
 				rows.forEach( row => {
 					let and_or = 'some';
 					if ( row.usergroup.startsWith( 'AND|' ) ) {
@@ -278,7 +278,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 						if ( verifynotice.logchannel ) {
 							useLogging = true;
 							result.logging.channel = verifynotice.logchannel.id;
-							if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+							if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 								let logembed = new MessageEmbed(embed);
 								if ( addRolesMentions[0].length ) logembed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 								if ( addRolesMentions[1].length ) logembed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
@@ -305,7 +305,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 							accountage: Math.trunc(accountage),
 							dateformat: lang.get('dateformat')
 						}).trim() : '' );
-						if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+						if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 							if ( addRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 							if ( addRolesMentions[1].length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
 							if ( removeRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_remove'), removeRolesMentions[0].join('\n') );
@@ -332,7 +332,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 
 				if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 					result.logging.channel = verifynotice.logchannel.id;
-					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						result.logging.embed = new MessageEmbed(embed);
 					}
 					else {
@@ -350,7 +350,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 					dateformat: lang.get('dateformat')
 				});
 				if ( !onmatch.trim() ) return;
-				if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
+				if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
 				else result.content += '\n\n**' + lang.get('verify.notice') + '** ' + onmatch;
 			}, error => {
 				if ( error ) console.log( '- Error while getting the Discord tag: ' + error );
@@ -363,7 +363,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 			result.content = error.reply;
 			if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 				result.logging.channel = verifynotice.logchannel.id;
-				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 					let logembed = new MessageEmbed(embed);
 					logembed.addField( lang.get('verify.discord', 'unknown'), escapeFormatting(member.user.tag) + ` (${member.toString()})`, true );
 					result.logging.embed = logembed;
@@ -379,7 +379,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 		
 		return got.get( wiki + 'api.php?action=query' + ( wiki.hasCentralAuth() ? '&meta=globaluserinfo&guiprop=groups&guiuser=' + encodeURIComponent( username ) : '' ) + '&prop=revisions&rvprop=content|user&rvslots=main&titles=User:' + encodeURIComponent( username ) + '/Discord&format=json' ).then( mwresponse => {
 			var mwbody = mwresponse.body;
-			if ( mwbody && mwbody.warnings ) log_warn(mwbody.warnings);
+			if ( mwbody && mwbody.warnings ) log_warning(mwbody.warnings);
 			if ( mwresponse.statusCode !== 200 || mwbody?.batchcomplete === undefined || !mwbody?.query?.pages ) {
 				console.log( '- ' + mwresponse.statusCode + ': Error while getting the Discord tag: ' + ( mwbody && mwbody.error && mwbody.error.info ) );
 				embed.setColor('#000000').setDescription( lang.get('verify.error') );
@@ -392,7 +392,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 					result.content = lang.get('verify.user_gblocked_reply', escapeFormatting(username), queryuser.gender);
 					if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 						result.logging.channel = verifynotice.logchannel.id;
-						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 							let logembed = new MessageEmbed(embed);
 							logembed.addField( lang.get('verify.discord', 'unknown'), escapeFormatting(member.user.tag) + ` (${member.toString()})`, true );
 							result.logging.embed = logembed;
@@ -421,7 +421,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 				embed.setColor('#FFFF00').setDescription( lang.get('verify.user_failed', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
 				if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
 					result.logging.channel = verifynotice.logchannel.id;
-					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						result.logging.embed = new MessageEmbed(embed);
 					}
 					else {
@@ -441,7 +441,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 			var removeRoles = [new Set(), new Set()];
 			var verified = false;
 			var rename = false;
-			var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86400000;
+			var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86_400_000;
 			rows.forEach( row => {
 				var and_or = 'some';
 				if ( row.usergroup.startsWith( 'AND|' ) ) {
@@ -509,7 +509,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 					if ( verifynotice.logchannel ) {
 						useLogging = true;
 						result.logging.channel = verifynotice.logchannel.id;
-						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 							var logembed = new MessageEmbed(embed);
 							if ( addRolesMentions[0].length ) logembed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 							if ( addRolesMentions[1].length ) logembed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
@@ -535,7 +535,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 						accountage: Math.trunc(accountage),
 						dateformat: lang.get('dateformat')
 					}).trim() : '' );
-					if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						if ( addRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 						if ( addRolesMentions[1].length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
 						if ( removeRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_remove'), removeRolesMentions[0].join('\n') );
@@ -567,7 +567,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 				dateformat: lang.get('dateformat')
 			});
 			if ( !onmatch.trim() ) return;
-			if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
+			if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
 			else result.content += '\n\n**' + lang.get('verify.notice') + '** ' + onmatch;
 		}, error => {
 			console.log( '- Error while getting the Discord tag: ' + error );
@@ -597,15 +597,16 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
  * @param {import('discord.js').CommandInteraction|import('discord.js').ButtonInteraction} [settings.interaction] - The interaction.
  * @param {Function} [settings.fail] - The function to call when the verifiction errors.
  * @param {import('discord.js').Message} [settings.sourceMessage] - The source message with the command.
+ * @global
  */
-global.verifyOauthUser = function(state, access_token, settings) {
+globalThis.verifyOauthUser = function(state, access_token, settings) {
 	if ( state && access_token && oauthVerify.has(state) ) {
 		settings = oauthVerify.get(state);
 		oauthVerify.delete(state);
 	}
 	if ( !settings?.channel ) return settings?.fail?.();
 	var channel = settings.channel;
-	if ( !channel.permissionsFor(channel.guild.me).has([FLAGS.VIEW_CHANNEL, FLAGS.SEND_MESSAGES]) ) return settings.fail?.();
+	if ( !channel.permissionsFor(channel.guild.me).has([Permissions.FLAGS.VIEW_CHANNEL, ( channel.isThread() ? Permissions.FLAGS.SEND_MESSAGES_IN_THREADS : Permissions.FLAGS.SEND_MESSAGES )]) ) return settings.fail?.();
 	Promise.all([
 		db.query( 'SELECT logchannel, flags, onsuccess, onmatch, role, editcount, postcount, usergroup, accountage, rename FROM verification LEFT JOIN verifynotice ON verification.guild = verifynotice.guild WHERE verification.guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [channel.guildId, '%|' + ( channel.isThread() ? channel.parentId : channel.id ) + '|%'] ).then( ({rows}) => {
 			if ( !rows.length ) return Promise.reject();
@@ -640,11 +641,11 @@ global.verifyOauthUser = function(state, access_token, settings) {
 		/** @type {{logchannel:import('discord.js').TextChannel,flags:Number,onsuccess:String,onmatch:String}} */
 		var verifynotice = ( rows[0] || {} );
 		verifynotice.logchannel = ( verifynotice.logchannel ? channel.guild.channels.cache.filter( logchannel => {
-			return ( logchannel.isGuild() && logchannel.permissionsFor(channel.guild.me).has([FLAGS.VIEW_CHANNEL, FLAGS.SEND_MESSAGES]) );
+			return ( logchannel.isText() && logchannel.permissionsFor(channel.guild.me).has([Permissions.FLAGS.VIEW_CHANNEL, Permissions.FLAGS.SEND_MESSAGES]) );
 		} ).get(verifynotice.logchannel) : null );
 		got.get( wiki + 'api.php?action=query&meta=siteinfo|globaluserinfo&siprop=general&guiprop=groups&guiuser=' + encodeURIComponent( username ) + '&list=users&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
 			var body = response.body;
-			if ( body && body.warnings ) log_warn(body.warnings);
+			if ( body && body.warnings ) log_warning(body.warnings);
 			if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.users?.[0] ) {
 				if ( wiki.noWiki(response.url, response.statusCode) ) {
 					console.log( '- This wiki doesn\'t exist!' );
@@ -658,7 +659,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 			logging(wiki, channel.guildId, 'verification');
 			var queryuser = body.query.users[0];
 			if ( body.query.users.length !== 1 || queryuser.missing !== undefined || queryuser.invalid !== undefined ) return settings.fail?.();
-			var embed = new MessageEmbed().setFooter( lang.get('verify.footer') ).setTimestamp().setAuthor( body.query.general.sitename ).addField( lang.get('verify.discord', queryuser.gender), escapeFormatting(member.user.tag), true ).addField( lang.get('verify.wiki', queryuser.gender), lang.get('verify.oauth_used'), true );
+			var embed = new MessageEmbed().setFooter( {text: lang.get('verify.footer')} ).setTimestamp().setAuthor( {name: body.query.general.sitename} ).addField( lang.get('verify.discord', queryuser.gender), escapeFormatting(member.user.tag), true ).addField( lang.get('verify.wiki', queryuser.gender), lang.get('verify.oauth_used'), true );
 			var pagelink = wiki.toLink('User:' + username, '', '', true);
 			embed.setTitle( escapeFormatting(username) ).setURL( pagelink );
 			if ( queryuser.blockexpiry ) {
@@ -667,7 +668,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					if ( (verifynotice.flags & 1 << 1) !== 1 << 1 || !verifynotice.logchannel ) return;
 					let logembed;
 					let logtext;
-					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						logembed = new MessageEmbed(embed);
 						logembed.addField( lang.get('verify.discord', 'unknown'), escapeFormatting(member.user.tag) + ` (${member.toString()})`, true );
 						if ( msg ) logembed.addField(msg.url, '<#' + channel.id + '>');
@@ -679,7 +680,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					}
 					verifynotice.logchannel.send( {
 						content: logtext,
-						embeds: [logembed]
+						embeds: ( logembed ? [logembed] : [] )
 					} ).catch(log_error);
 				}, log_error );
 			}
@@ -689,7 +690,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					if ( (verifynotice.flags & 1 << 1) !== 1 << 1 || !verifynotice.logchannel ) return;
 					let logembed;
 					let logtext;
-					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						logembed = new MessageEmbed(embed);
 						logembed.addField( lang.get('verify.discord', 'unknown'), escapeFormatting(member.user.tag) + ` (${member.toString()})`, true );
 						if ( msg ) logembed.addField(msg.url, '<#' + channel.id + '>');
@@ -701,7 +702,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					}
 					verifynotice.logchannel.send( {
 						content: logtext,
-						embeds: [logembed]
+						embeds: ( logembed ? [logembed] : [] )
 					} ).catch(log_error);
 				}, log_error );
 			}
@@ -713,7 +714,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 			var removeRoles = [new Set(), new Set()];
 			var verified = false;
 			var rename = false;
-			var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86400000;
+			var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86_400_000;
 			rows.forEach( row => {
 				var and_or = 'some';
 				if ( row.usergroup.startsWith( 'AND|' ) ) {
@@ -783,7 +784,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					var logtext;
 					if ( verifynotice.logchannel ) {
 						useLogging = true;
-						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+						if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 							logembed = new MessageEmbed(embed);
 							if ( addRolesMentions[0].length ) logembed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 							if ( addRolesMentions[1].length ) logembed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
@@ -808,7 +809,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 						accountage: Math.trunc(accountage),
 						dateformat: lang.get('dateformat')
 					}).trim() : '' );
-					if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						if ( addRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_add'), addRolesMentions[0].join('\n') );
 						if ( addRolesMentions[1].length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.qualified_add_error'), addRolesMentions[1].join('\n') );
 						if ( removeRolesMentions[0].length ) embed.addField( lang.get('verify.qualified_remove'), removeRolesMentions[0].join('\n') );
@@ -833,7 +834,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 						}
 						verifynotice.logchannel.send( {
 							content: logtext,
-							embeds: [logembed]
+							embeds: ( logembed ? [logembed] : [] )
 						} ).catch(log_error);
 					}, log_error );
 				}, log_error );
@@ -844,7 +845,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 			let logembed;
 			let logtext;
 			if ( (verifynotice.flags & 1 << 1) === 1 << 1 && verifynotice.logchannel ) {
-				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+				if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 					logembed = new MessageEmbed(embed);
 				}
 				else {
@@ -861,7 +862,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 					dateformat: lang.get('dateformat')
 				});
 				if ( onmatch.trim() ) {
-					if ( channel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
+					if ( channel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) embed.addField( lang.get('verify.notice'), onmatch );
 					else noticeContent = '\n\n**' + lang.get('verify.notice') + '** ' + onmatch;
 				}
 			}
@@ -873,14 +874,14 @@ global.verifyOauthUser = function(state, access_token, settings) {
 			} ).then( msg => {
 				if ( !logtext && !logembed ) return;
 				if ( msg ) {
-					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(FLAGS.EMBED_LINKS) ) {
+					if ( verifynotice.logchannel.permissionsFor(channel.guild.me).has(Permissions.FLAGS.EMBED_LINKS) ) {
 						logembed.addField(msg.url, '<#' + channel.id + '>');
 					}
 					else logtext += '\n<#' + channel.id + '> – <' + msg.url + '>';
 				}
 				verifynotice.logchannel.send( {
 					content: logtext,
-					embeds: [logembed]
+					embeds: ( logembed ? [logembed] : [] )
 				} ).catch(log_error);
 			}, log_error );
 
@@ -925,7 +926,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 							allowDelete(msg, member.id);
 							if ( settings.sourceMessage ) {
 								settings.sourceMessage.reactEmoji('📩');
-								setTimeout( () => settings.sourceMessage.delete().catch(log_error), 60000 ).unref();
+								setTimeout( () => settings.sourceMessage.delete().catch(log_error), 60_000 ).unref();
 							}
 						}, error => {
 							if ( error?.code === 50007 ) { // CANNOT_MESSAGE_USER
@@ -951,7 +952,7 @@ global.verifyOauthUser = function(state, access_token, settings) {
 						allowDelete(msg, member.id);
 						if ( settings.sourceMessage ) {
 							settings.sourceMessage.reactEmoji('📩');
-							setTimeout( () => settings.sourceMessage.delete().catch(log_error), 60000 ).unref();
+							setTimeout( () => settings.sourceMessage.delete().catch(log_error), 60_000 ).unref();
 						}
 					}, error => {
 						if ( error?.code === 50007 ) { // CANNOT_MESSAGE_USER
@@ -987,7 +988,7 @@ function parseNotice(text = '', variables = {editcount: 0, postcount: 0, account
 	text = text.replace( /\$(editcount|postcount|accountage)/g, (variable, key, offset, fulltext) => {
 		var value = ( variables[key] ?? 0 );
 		if ( typeof value === 'string' ) return value;
-		if ( /#(?:if)?expr:[^{|}]*$/.test(fulltext.substring(0, offset)) ) return ( value > 1000000000 ? 1000000000 : value );
+		if ( /#(?:if)?expr:[^{|}]*$/.test(fulltext.substring(0, offset)) ) return ( value > 1_000_000_000 ? 1_000_000_000 : value );
 		return value.toLocaleString(variables.dateformat);
 	} );
 	if ( text.includes( '#expr:' ) ) text = text.replace( /{{\s*#expr:\s*(-?\d{1,10})\s*([+-])\s*(-?\d{1,10})(?:\s*([+-])\s*(-?\d{1,10}))?(?:\s*([+-])\s*(-?\d{1,10}))?(?:\s*([+-])\s*(-?\d{1,10}))?\s*}}/g, (expr, n0, o1, n1, o2, n2, o3, n3, o4, n4, offset, fulltext) => {
@@ -1050,6 +1051,4 @@ function ifexpr(number, operator) {
 			break;
 	}
 	return result;
-}
-
-module.exports = verify;
+}

+ 5 - 0
i18n/allLangs.json

@@ -7,6 +7,7 @@
 			"es": "Español (es)",
 			"fr": "Français (fr)",
 			"hi": "हिन्दी (hi)",
+			"it": "Italiano (it)",
 			"ko": "한국어 (ko)",
 			"pl": "Polski (pl)",
 			"pt-br": "Português do Brasil (pt-br)",
@@ -44,6 +45,10 @@
 			"hindi": "hi",
 			"हिन्दी": "hi",
 			"हिन्दी (hi)": "hi",
+			"it": "it",
+			"italian":"it",
+			"italiano": "it",
+			"italiano (it)": "it",
 			"ko": "ko",
 			"korean": "ko",
 			"한국어": "ko",

+ 22 - 2
i18n/bn.json

@@ -138,7 +138,7 @@
         "votes": "$1 {{PLURAL:$2|ভোট|টি ভোট}} ($3%)"
     },
     "fallback": [
-        "en",
+        " ",
         " ",
         " ",
         " ",
@@ -494,6 +494,8 @@
         "all_inactive": "আপনি উইকি পরিবর্তন অর ফিড-আধারিত পরিবর্তন এক বাড়ে অক্ষম রাখতে পারবেন না।",
         "audit_reason": "\"$1\"এর জন্যে রিসেন্ট চেঞ্জেস ওয়েবহুক",
         "audit_reason_delete": "রিসেন্ট চেঞ্জেস ওয়েবহুক সরিয়ে দেওয়া হয়েছে",
+        "audit_reason_edit": "আপডেটকৃত রিসেন্ট চেঞ্জেস ওয়েবহুক",
+        "avatar": "ওয়েবহুক ছবি:",
         "blocked": "এই উইকিকে রিসেন্ট চেঞ্জেস ওয়েবহুকের মধ্যে হওয়ার থেকে ব্ল'ক করা হয়েছে!",
         "blocked_reason": "এই উইকিকে `$1`কারণের জন্যে রিসেন্ট চেঞ্জেস ওয়েবহুকের মধ্যে হওয়ার থেকে ব্ল'ক করা হয়েছে!",
         "channel": "চ্যানেল:",
@@ -527,6 +529,7 @@
         "lang": "ভাষা:",
         "max_entries": "আপনি রিসেন্ট চেঞ্জেস ওয়েবহুকের সর্বাধিক পরিমানে পৌঁছে গেছেন।",
         "missing": "এই সার্ভারের জন্যে এখন পর্যন্ত কোনও রিসেন্ট চেঞ্জেস ওয়েবহুক নেই।",
+        "name": "ওয়েবহুকের নাম:",
         "new_lang": "<নতুন ভাষা>",
         "new_wiki": "<উইকির লিংক>",
         "no_feeds": "এই ওয়েবহুকের উইকির জন্যে কোনও ফিড-আধারিত পরিবর্তন, যেমন ডিসকাশন, মেসেজ ওয়াল বা আর্টিকেল কমেন্ট সক্ষম নেই।",
@@ -544,6 +547,7 @@
             "blocked_reason": "এই রিসেন্ট চেঞ্জেস ওয়েবহুকটি কে ডিলিট করে দেওয়া হবে কারণ এই উইকিটি কে `$1`এর জন্যে ব্ল'ক করে দেওয়া হয়েছে!",
             "created": "এই চ্যানেলে $1এর জন্যে একটি রিসেন্ট চেঞ্জেস ওয়েবহুক লাগানো হয়েছে।",
             "dashboard": {
+                "avatar": "• ওয়েবহুক ছবি পরিবর্তন করা হয়েছে।",
                 "channel": "• ওয়েবহুকটিকে এই চ্যানেলে নিয়ে আসা হয়েছে।",
                 "disabled_feeds": "• ফিড-আধারিত পরিবর্তন, যেমন ডিসকাশন, মেসেজ ওয়াল, আর আর্টিকেল কমেন্ট, এখন অক্ষম হয়ে গেছে।",
                 "disabled_rc": "• উইকি পরিবর্তন এখন অক্ষম।",
@@ -554,6 +558,7 @@
                 "enabled_feeds": "• ফিড-আধারিত পরিবর্তন, যেমন ডিসকাশন, মেসেজ ওয়াল, আর আর্টিকেল কমেন্ট, এখন সক্ষম।",
                 "enabled_rc": "• উইকি পরিবর্তন এখন সক্ষম।",
                 "lang": "• ভাষাকে $1এ বদলে দেওয়া হয়েছে।",
+                "name": "• ওয়েবহুকের নাম পরিবর্তন করে \"$1\" করা হয়েছে।",
                 "updated": "এই রিসেন্ট চেঞ্জেস ওয়েবহুকটিকে আপডেট করা হয়েছে:",
                 "wiki": "• উইকিটিকে $1এ বদলে দেওয়া হয়েছে।"
             },
@@ -573,6 +578,7 @@
         "wiki": "উইকি:"
     },
     "search": {
+        "cached": "ক্যাশ সর্বশেষ আপডেট হয়েছে:",
         "category": {
             "content": "এই শ্রেণীর কনটেন্ট:",
             "empty": "*এই শ্রেণী খালি*",
@@ -589,6 +595,7 @@
         "special": "এই বিশেষ পৃষ্ঠের কনটেন্ট:"
     },
     "settings": {
+        "button": "ড্যাশবোর্ড ব্যবহার করুন",
         "channel current": "এগুলি হলো বর্তমান চ্যানেলের সেটিংস:",
         "channel lang": "এই চ্যানেলের ভাষা হলো:",
         "channel langchanged": "আপনি এই চ্যানেলের ভাষা কে এটিতে বদলেছেন:",
@@ -738,15 +745,21 @@
         "current_selected": "এটি এই সার্ভারের ভেরিফিকেশন `$1`:",
         "dashboard": {
             "added": "$1 `$2` ID দিয়ে ভেরিফিকেশন বানিয়েছে।",
+            "added_notice": "$1 কিছু যাচাইকরণ বিজ্ঞপ্তি যোগ করেছে।",
             "removed": "$1এ ID `$2` দিয়ে ভেরিফিকেশন সরিয়ে দিয়েছে।",
-            "updated": "$1 ID `$2` দিয়ে ভেরিফিকেশন আপডেট করেছে।"
+            "updated": "$1 ID `$2` দিয়ে ভেরিফিকেশন আপডেট করেছে।",
+            "updated_notice": "$1 কিছু যাচাইকরণ বিজ্ঞপ্তি আপডেট করেছে।"
         },
         "delete_current": "এই ভেরিফিকেশনটিকে ডিলিট করে দেন:",
         "deleted": "এই ভেরিফিকেশনটিকে ডিলিট করে দেওয়া হয়েছে।",
         "disabled": "অক্ষম",
         "editcount": "সম্পাদনার মাত্রা:",
         "enabled": "সক্ষম",
+        "flag_logall": "ব্যর্থ যাচাইকরণ লগিং:",
+        "flag_private": "যাচাইকরণ কমান্ড প্রতিক্রিয়া ব্যক্তিগতভাবে পাঠানো:",
         "indays": "(দিনে)",
+        "logging": "লগিং চ্যানেল:",
+        "match": "নিরুদ্দিষ্ট প্রয়োজনীয়তা বিজ্ঞপ্তি:",
         "max_entries": "আপনি ভেরিফিকেশনের অধিকতাম মাত্রই পৌঁছে গেছেন।",
         "missing": "এই সার্ভারের জন্যে এখন পর্যন্ত কোনও ভেরিফিকেশন নেই।",
         "new_accountage": "<নতুন একাউন্টের বয়স>",
@@ -756,18 +769,23 @@
         "new_role": "<নতুন রোল>",
         "new_usergroup": "<নতুন সদস্য গ্রুপ>",
         "no_role": "অনুগ্রহ করে নতুন ভেরিফিকেশনের জন্যে একটি রোল দেন।",
+        "notice_embed": "কিছু বিজ্ঞপ্তিতে গোপন মার্কডাউন লিঙ্ক আছে। নিশ্চিত করুন যে বটটির সমস্ত যাচাইকরণ চ্যানেলে `এম্বেড লিঙ্কস` অনুমতি রয়েছে যাতে সেগুলি সঠিকভাবে কাজ করতে পারে।",
         "or": "বা",
         "postcount": "পোস্টের মাত্রা (শুধু ফ্যানডম উইকির জন্য):",
         "postcount_or": "(সম্পাদনার মাত্রার পরে একটা দ্বিতীয় বিকল্প)",
         "posteditcount": "সম্পাদনা আর পোস্টের মাত্রা মিলিয়ে:",
         "rename": "নিকনেম বদলান:",
         "rename_no_permission": "**$1এর কাছে উইকি সদস্যনামে জোর করার জন্যে `Manage Nicknames` অনুমতি নেই!**",
+        "role_add": "রোল যোগ করতে:",
         "role_deleted": "**মনে হয় রোল $1 আর উপস্থিত নয়!**",
         "role_managed": "এই রোল দেওয়া যাবে না।",
         "role_max": "আপনি কিছু বেশিই রোল দিয়ে দিয়েছেন।",
         "role_missing": "এই রোলে পাওয়া যায়নি।",
+        "role_none": "কোনটি না",
+        "role_remove": "রোল সরাতে:",
         "role_too_high": "**রোল $1 $2র দেওয়ার জন্যে অনেক উঁচু!**",
         "save_failed": "দুঃখজনকভাবে ভেরিফিকেশন এখন অসফল হলো, অনুগ্রহ করে কিছু সময় পরে চেষ্টা করুন।",
+        "success": "সাফল্যের বিজ্ঞপ্তি:",
         "toggle": "(টগ্যাল)",
         "updated": "ভেরিফিকেশন আপডেট হয়ে গেছে:",
         "usergroup": "ইউসার গ্রুপ:",
@@ -777,6 +795,8 @@
     },
     "verify": {
         "audit_reason": "\"$1\"এর রোপ ভেরিফাইড",
+        "button_again": "আবার দেখুন",
+        "button_wrong_user": "আপনি এই যাচাইকরণ আবার পরীক্ষা করতে পারবেন না, এটি $1 এর যাচাইকরণ!",
         "discord": "ডিসকোর্ড ব্যবহারকারী:",
         "empty": "*খালি*",
         "error": "ভেরিফিকেশন একটি ত্রুটির জন্যে হলোনা।",

+ 94 - 91
i18n/de.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "de-DE",
     "diff": {
-        "badrev": "mindestens eine Version existiert nicht!",
+        "badrev": "Mindestens eine Version existiert nicht!",
         "hidden": "*versteckt*",
         "info": {
             "added": "Hinzugefügt:",
@@ -146,15 +146,15 @@
     ],
     "general": {
         "database": "⚠️ **Eingeschränkte Funktionalität** ⚠️\nEs wurden keine Einstellungen gefunden, bitte kontaktiere den Bot-Besitzer!",
-        "default": "für diesen Server wurden noch keine Einstellungen vorgenommen. Nutze $1 oder die Webseite um die Einstellungen vorzunehmen.",
+        "default": "Für diesen Server wurden noch keine Einstellungen vorgenommen. Nutze $1 oder die Webseite um die Einstellungen vorzunehmen.",
         "disclaimer": "Ich bin ein kleiner Bot mit der Aufgabe einfach auf MediaWiki Websites, wie Wikipedia und Fandom-Wikis, zu verlinken und diese zu durchsuchen. Ich zeige kurze Beschreibungen und zusätzliche Informationen zu Seiten und kann Weiterleitungen auflösen sowie Interwiki-Links folgen. Geschrieben wurde ich von $1 in JavaScript.\n\nDu kannst mich auf Patreon unterstützen:",
         "experimental": "**Diese Funktion ist experimentell! Sie ist nicht garantiert richtig zu funktionieren und könnte in Zukunft eventuell entfernt werden.**",
         "helpserver": "Bei Fragen oder Problemen besuche bitte meinen Support-Server:",
         "limit": "🚨 **Stop, du hast ein Limit erreicht!** 🚨\n\n$1, deine Nachricht enthält zu viele Befehle!",
         "missingperm": "mir fehlen einige Berechtigungen für diesen Befehl:",
-        "patreon": "dies ist eine Patreon-Funktion!\nDu kannst mich auf Patreon unterstützen um Zugang zu dieser Funktion zu erhalten:",
-        "prefix": "das Präfix für diesen Server ist `$1`. Du kannst das Präfix mit `$1settings prefix` ändern. Für eine Liste aller Befehle nutze `$1hilfe`.",
-        "readonly": "**die Datenbank ist derzeit im Nur-Lese-Modus, du kannst aktuell keine Einstellungen ändern!**"
+        "patreon": "Dies ist eine Patreon-Funktion!\nDu kannst mich auf Patreon unterstützen um Zugang zu dieser Funktion zu erhalten:",
+        "prefix": "Das Präfix für diesen Server ist `$1`. Du kannst das Präfix mit `$1settings prefix` ändern. Für eine Liste aller Befehle nutze `$1hilfe`.",
+        "readonly": "**Die Datenbank ist derzeit im Nur-Lese-Modus, du kannst aktuell keine Einstellungen ändern!**"
     },
     "help": {
         "admin": "Diese Befehle können nur Administratoren ausführen:",
@@ -389,7 +389,7 @@
                 "desc": "Ich antworte mit einem Link auf einen passenden Artikel im angegebenen Wikia-Wiki: `https://<Wiki>.wikia.org/`"
             }
         },
-        "noadmin": "du benötigst die `Server verwalten`-Berechtigung für diese Befehle!",
+        "noadmin": "Du benötigst die `Server verwalten`-Berechtigung für diese Befehle!",
         "pause": "**Ich bin auf diesem Server derzeit pausiert!**\nNur diese Befehle können ausgeführt werden:"
     },
     "interaction": {
@@ -490,34 +490,34 @@
     "rcscript": {
         "ad": "Du möchtest letzte Änderungen direkt in Discord? Nutze `$1rcscript` um einen Letzte Änderungen-WebHook basierend auf **$2** zu deinen Diesocrd server hinzuzufügen!",
         "add_more": "Füge mehr Letzte Änderungen-WebHooks hinzu:",
-        "added": "ein Letzte Änderungen-WebHook wurde hinzugefügt für:",
-        "all_inactive": "du kannst Wiki-Änderungen und Feeds-basierte Änderungen nicht gleichzeitig deaktiviert haben.",
+        "added": "Ein Letzte Änderungen-WebHook wurde hinzugefügt für:",
+        "all_inactive": "Du kannst Wiki-Änderungen und Feeds-basierte Änderungen nicht gleichzeitig deaktiviert haben.",
         "audit_reason": "Letzte Änderungen-WebHook für \"$1\"",
         "audit_reason_delete": "Letzte Änderungen-WebHook entfernt",
         "audit_reason_edit": "Letzte Änderungen-WebHook aktualisiert",
         "avatar": "WebHook-Avatar:",
-        "blocked": "diese Wiki wurde davon gesperrt als Letzte Änderungen-WebHook hinzugefügt zu werden!",
-        "blocked_reason": "diese Wiki wurde wegen `$1` davon gesperrt als Letzte Änderungen-WebHook hinzugefügt zu werden!",
+        "blocked": "Diese Wiki wurde davon gesperrt als Letzte Änderungen-WebHook hinzugefügt zu werden!",
+        "blocked_reason": "Diese Wiki wurde wegen `$1` davon gesperrt als Letzte Änderungen-WebHook hinzugefügt zu werden!",
         "channel": "Kanal:",
-        "current": "dies sind die aktuellen Letzte Änderungen-WebHooks für diesen Server:",
-        "current_display": "der Anzeigemodus für diesen WebHook ist:",
-        "current_lang": "die Sprache für diesen WebHook ist:",
-        "current_selected": "dies ist der Letzte Änderungen-WebHook `$1` für diesen Server:",
-        "current_wiki": "das Wiki für diesen WebHook ist:",
+        "current": "Dies sind die aktuellen Letzte Änderungen-WebHooks für diesen Server:",
+        "current_display": "Der Anzeigemodus für diesen WebHook ist:",
+        "current_lang": "Die Sprache für diesen WebHook ist:",
+        "current_selected": "Dies ist der Letzte Änderungen-WebHook `$1` für diesen Server:",
+        "current_wiki": "Das Wiki für diesen WebHook ist:",
         "dashboard": {
             "added": "$1 hat den Letzte Änderungen-WebHook mit der ID `$2` hinzugefügt.",
             "removed": "$1 hat den Letzte Änderungen-WebHook mit der ID `$2` gelöscht.",
             "updated": "$1 hat den Letzte Änderungen-WebHook mit der ID `$2` bearbeitet."
         },
         "delete": "Lösche diesen Letzte Änderungen-WebHook:",
-        "deleted": "der Letzte Änderungen-WebHook wurde gelöscht.",
+        "deleted": "Der Letzte Änderungen-WebHook wurde gelöscht.",
         "disabled": "deaktiviert",
-        "disabled_feeds": "die Feeds-basierten Änderungen, wie Diskussionen, Nachrichtenseiten und Artikelkommentare, für diesen WebHook wurden deaktiviert.",
-        "disabled_rc": "die Wiki-Änderungen für diesen WebHook wurden deaktiviert.",
+        "disabled_feeds": "Die Feeds-basierten Änderungen, wie Diskussionen, Nachrichtenseiten und Artikelkommentare, für diesen WebHook wurden deaktiviert.",
+        "disabled_rc": "Die Wiki-Änderungen für diesen WebHook wurden deaktiviert.",
         "display": "Anzeigemodus:",
         "enabled": "aktiviert",
-        "enabled_feeds": "die Feeds-basierten Änderungen, wie Diskussionen, Nachrichtenseiten und Artikelkommentare, für diesen WebHook wurden aktiviert.",
-        "enabled_rc": "die Wiki-Änderungen für diesen WebHook wurden aktiviert.",
+        "enabled_feeds": "Die Feeds-basierten Änderungen, wie Diskussionen, Nachrichtenseiten und Artikelkommentare, für diesen WebHook wurden aktiviert.",
+        "enabled_rc": "Die Wiki-Änderungen für diesen WebHook wurden aktiviert.",
         "feeds": "Feeds-basierte Änderungen:",
         "help_display_compact": "Kompakte Textnachrichten mit Inline-Links.",
         "help_display_diff": "Einbettungen mit Bildvorschau und Bearbeitungsunterschieden.",
@@ -527,20 +527,20 @@
         "help_lang": "Bisher mögliche Sprachen sind:",
         "help_wiki": "Link zu einer MediaWiki Website, wie `https://<Wiki>.fandom.com/`",
         "lang": "Sprache:",
-        "max_entries": "du hast bereits die maximale Anzahl an Letzte Änderungen-WebHooks erreicht.",
-        "missing": "es ist noch kein Letzte Änderungen-WebHook für diesen Server vorhanden.",
+        "max_entries": "Du hast bereits die maximale Anzahl an Letzte Änderungen-WebHooks erreicht.",
+        "missing": "Es ist noch kein Letzte Änderungen-WebHook für diesen Server vorhanden.",
         "name": "WebHook-Name:",
         "new_lang": "<neue Sprache>",
         "new_wiki": "<Link zum Wiki>",
-        "no_feeds": "das Wiki für diesen WebHook hat keine Feeds-basierten Funktionen, wie Diskussionen, Nachrichtenseiten oder Artikelkommentare, aktiviert.",
-        "noadmin": "du benötigst die `WebHooks verwalten`-Berechtigung für diesen Befehl!",
+        "no_feeds": "Das Wiki für diesen WebHook hat keine Feeds-basierten Funktionen, wie Diskussionen, Nachrichtenseiten oder Artikelkommentare, aktiviert.",
+        "noadmin": "Du benötigst die `WebHooks verwalten`-Berechtigung für diesen Befehl!",
         "rc": "Wiki-Änderungen:",
-        "sysmessage": "die Systemnachricht `$1` muss die Server-ID `$2` sein um einen Letzte Änderungen-WebHook hinzuzufügen.",
+        "sysmessage": "Die Systemnachricht `$1` muss die Server-ID `$2` sein um einen Letzte Änderungen-WebHook hinzuzufügen.",
         "title": "Letzte Änderungen-WebHook",
         "toggle": "(umschalten)",
-        "updated_display": "der Anzeigemodus für diesen WebHook wurde geändert zu:",
-        "updated_lang": "die Sprache für diesen WebHook wurde geändert zu:",
-        "updated_wiki": "das Wiki für diesen WebHook wurde geändert zu:",
+        "updated_display": "Der Anzeigemodus für diesen WebHook wurde geändert zu:",
+        "updated_lang": "Die Sprache für diesen WebHook wurde geändert zu:",
+        "updated_wiki": "Das Wiki für diesen WebHook wurde geändert zu:",
         "webhook": {
             "blocked": "Dieser Letzte Änderungen-WebHook wird gelöscht, da das Wiki gesperrt wurde!",
             "blocked_help": "Du kannst auf dem [Support-Server]($1) nach mehr Details fragen.",
@@ -574,7 +574,7 @@
             "updated_lang": "Die Sprache für diesen Letzte Änderungen-WebHook wurde zu `$1` geändert.",
             "updated_wiki": "Das Wiki für diesen Letzte Änderungen-WebHook wurde zu $1 geändert."
         },
-        "webhook_failed": "der WebHook konnten leider nicht erstellt werden, bitte versuche es später erneut.",
+        "webhook_failed": "Der WebHook konnten leider nicht erstellt werden, bitte versuche es später erneut.",
         "wiki": "Wiki:"
     },
     "search": {
@@ -591,19 +591,21 @@
         "infosearch": "Nicht das richtige Ergebnis? Nutze $1 für einen direkten Link oder $2 für eine Liste mit allen Treffern.",
         "loading": "Lade Seitenbeschreibung…",
         "media": "Zur Dateibeschreibungsseite",
+        "messagedefault": "Standardwert:",
+        "messagedefaultnone": "*keiner*",
         "results": "$1 {{PLURAL:$2|Ergebnis|Ergebnisse}} insgesamt",
         "special": "Inhalt der Spezialseite:"
     },
     "settings": {
         "button": "Nutze das Dashboard",
-        "channel current": "dies sind die aktuellen Einstellungen für diesen Kanal:",
-        "channel lang": "die Sprache für diesen Kanal ist:",
-        "channel langchanged": "du hast die Sprache für diesen Kanal geändert zu:",
-        "channel role": "die mindestens benötigte Rolle für diesen Kanal ist:",
-        "channel rolechanged": "du hast die mindestens benötigte Rolle für diesen Kanal geändert zu:",
-        "channel wiki": "das Standard-Wiki für diesen Kanal ist:",
-        "channel wikichanged": "du hast das Standard-Wiki für diesen Kanal geändert zu:",
-        "current": "dies sind die aktuellen Einstellungen für diesen Server:",
+        "channel current": "Dies sind die aktuellen Einstellungen für diesen Kanal:",
+        "channel lang": "Die Sprache für diesen Kanal ist:",
+        "channel langchanged": "Du hast die Sprache für diesen Kanal geändert zu:",
+        "channel role": "Die mindestens benötigte Rolle für diesen Kanal ist:",
+        "channel rolechanged": "Du hast die mindestens benötigte Rolle für diesen Kanal geändert zu:",
+        "channel wiki": "Das Standard-Wiki für diesen Kanal ist:",
+        "channel wikichanged": "Du hast das Standard-Wiki für diesen Kanal geändert zu:",
+        "current": "Dies sind die aktuellen Einstellungen für diesen Server:",
         "currentchannel": "Kanal-Überschreibungen:",
         "currentinline": "Inline-Befehle:",
         "currentlang": "Sprache:",
@@ -617,54 +619,55 @@
         },
         "foundwikis": "Meinst du eines dieser Wikis?",
         "inline disabled": {
-            "channel inline": "Inline-Befehle sind für diesen Kanal deaktiviert.",
-            "channel inlinechanged": "du hast Inline-Befehle für diesen Kanal deaktiviert.",
+            "channel inline": "Die Inline-Befehle sind für diesen Kanal deaktiviert.",
+            "channel inlinechanged": "Du hast Inline-Befehle für diesen Kanal deaktiviert.",
             "help": "Nutze `$1` um Inline-Befehle wie `[[$2]]` und `{{$2}}` zu aktivieren.",
-            "inline": "Inline-Befehle sind für diesen Server deaktiviert.",
-            "inlinechanged": "du hast Inline-Befehle für diesen Server deaktiviert."
+            "inline": "Die Inline-Befehle sind für diesen Server deaktiviert.",
+            "inlinechanged": "Du hast Inline-Befehle für diesen Server deaktiviert."
         },
         "inline enabled": {
-            "channel inline": "Inline-Befehle sind für diesen Kanal aktiviert.",
-            "channel inlinechanged": "du hast Inline-Befehle für diesen Kanal aktiviert.",
+            "channel inline": "Die Inline-Befehle sind für diesen Kanal aktiviert.",
+            "channel inlinechanged": "Du hast Inline-Befehle für diesen Kanal aktiviert.",
             "help": "Nutze `$1` um Inline-Befehle wie `[[$2]]` und `{{$2}}` zu deaktivieren.",
-            "inline": "Inline-Befehle sind für diesen Server aktiviert.",
-            "inlinechanged": "du hast Inline-Befehle für diesen Server aktiviert."
+            "inline": "Die Inline-Befehle sind für diesen Server aktiviert.",
+            "inlinechanged": "Du hast Inline-Befehle für diesen Server aktiviert."
         },
-        "lang": "die Sprache für diesen Server ist:",
-        "langchanged": "du hast die Sprache für diesen Server geändert zu:",
+        "lang": "Die Sprache für diesen Server ist:",
+        "langchanged": "Du hast die Sprache für diesen Server geändert zu:",
         "langhelp": "Nutze `$1 <Sprache>` um die Sprache zu ändern.\nBisher mögliche Sprachen sind:",
-        "langinvalid": "die angegebende Sprache wird nicht unterstützt!",
-        "missing": "für diesen Server wurden noch keine Einstellungen vorgenommen. Nutze $1 und $2 um die Einstellungen vorzunehmen.",
+        "langinvalid": "Die angegebene Sprache wird nicht unterstützt!",
+        "missing": "Für diesen Server wurden noch keine Einstellungen vorgenommen. Nutze $1 und $2 um die Einstellungen vorzunehmen.",
         "nochannels": "*Noch keine Kanal-Überschreibungen*",
-        "prefix": "das Präfix für diesen Server ist:",
-        "prefixchanged": "du hast das Präfix für diesen Server geändert zu:",
+        "prefix": "Das Präfix für diesen Server ist:",
+        "prefixchanged": "Du hast das Präfix für diesen Server geändert zu:",
         "prefixhelp": "Nutze `$1 <Präfix>` um das Präfix zu ändern.\nNutze `_` am Ende um ein Leerzeichen am Ende des Präfixes anzugeben.\nDas Präfix darf keine Erwähnungen enthalten!",
-        "prefixinvalid": "das angegebende Präfix wird nicht unterstützt!",
+        "prefixinvalid": "Das angegebene Präfix wird nicht unterstützt!",
         "role": "die mindestens benötigte Rolle für diesen Server ist:",
-        "rolechanged": "du hast die mindestens benötigte Rolle für diesen Server geändert zu:",
+        "rolechanged": "Du hast die mindestens benötigte Rolle für diesen Server geändert zu:",
         "rolehelp": "Nutze `$1 <Rolle>` um die mindestens benötigte Rolle zu ändern.",
-        "roleinvalid": "die angegebene Rolle existiert nicht!",
-        "save_failed": "die Einstellungen konnten leider nicht gespeichert werden, bitte versuche es später erneut.",
-        "wiki": "das Standard-Wiki für diesen Server ist:",
-        "wikichanged": "du hast das Standard-Wiki für diesen Server geändert zu:",
+        "roleinvalid": "Die angegebene Rolle existiert nicht!",
+        "save_failed": "Die Einstellungen konnten leider nicht gespeichert werden, bitte versuche es später erneut.",
+        "wiki": "Das Standard-Wiki für diesen Server ist:",
+        "wikichanged": "Du hast das Standard-Wiki für diesen Server geändert zu:",
         "wikihelp": "Nutze `$1 <Link>` um das Standard-Wiki zu ändern.\nLink zu einer MediaWiki Website, wie `https://<Wiki>.fandom.com/`",
-        "wikiinvalid": "bitte gib einen gültigen Link zu einer MediaWiki Website, wie Wikipedia oder ein Fandom-Wiki, an!",
-        "wikiinvalid_http": "die angegebene Website besitzt kein gültiges TLS/SSL-Zertifikat! Aus Sicherheitsgründen werden nur Wikis die HTTPS nutzen unterstützt.\nFalls du ein Website-Administrator bist kannst du ein Zertifikat von einer Zertifizierungsstelle wie *Let’s Encrypt* erhalten:\n<https://letsencrypt.org/de/getting-started/>",
-        "wikiinvalid_private": "das angegebene Wiki ist privat! Nur öffentliche Wikis, die von jedem gelesen werden können, sind unterstützt.",
-        "wikiinvalid_timeout": "der angegebene Link hat zu lange gebraucht um zu antworten!",
-        "wikimissing": "für diesen Server wurde noch kein Wiki festgelegt!"
+        "wikiinvalid": "Bitte gib einen gültigen Link zu einer MediaWiki Website, wie Wikipedia oder ein Fandom-Wiki, an!",
+        "wikiinvalid_http": "Die angegebene Website besitzt kein gültiges TLS/SSL-Zertifikat! Aus Sicherheitsgründen werden nur Wikis die HTTPS nutzen unterstützt.\nFalls du ein Website-Administrator bist kannst du ein Zertifikat von einer Zertifizierungsstelle wie *Let’s Encrypt* erhalten:\n<https://letsencrypt.org/de/getting-started/>",
+        "wikiinvalid_private": "Das angegebene Wiki ist privat! Nur öffentliche Wikis, die von jedem gelesen werden können, sind unterstützt.",
+        "wikiinvalid_timeout": "Der angegebene Link hat zu lange gebraucht um zu antworten!",
+        "wikimissing": "Für diesen Server wurde noch kein Wiki festgelegt!"
     },
     "test": {
         "MediaWiki": "Benötigt mindestens $1 für vollständige Funktionalität, fand `$2`.",
         "notice": "Eingeschränkte Funktionalität",
         "pause": "ich bin auf diesem Server derzeit pausiert.",
+        "slow": "Entschuldigung, ich scheine derzeit etwas langsam zu sein.",
         "text": [
-            "ich bin voll funktionsfähig!",
-            "ich bin ja schon wach!",
-            "du hast mich gerufen?",
-            "ja, ich funktioniere noch!",
-            "hast du **Kekse** gesagt?",
-            "hast du **Kekse** gesagt?",
+            "Ich bin voll funktionsfähig!",
+            "Ich bin ja schon wach!",
+            "Du hast mich gerufen?",
+            "Ja, ich funktioniere noch!",
+            "Hast du **Kekse** gesagt?",
+            "Hast du **Kekse** gesagt?",
             " ",
             " ",
             " ",
@@ -736,13 +739,13 @@
     "verification": {
         "accountage": "Account-Alter:",
         "add_more": "Füge mehr Verifizierungen hinzu:",
-        "added": "die Verifizierung wurde hinzugefügt:",
+        "added": "Die Verifizierung wurde hinzugefügt:",
         "and": "und",
         "channel": "Kanal:",
-        "channel_max": "du hast zu viele Kanäle angegeben.",
-        "channel_missing": "der angegebe Kanal existiert nicht.",
-        "current": "dies sind die aktuellen Verifizierungen für diesen Server:",
-        "current_selected": "dies ist die Verifizierung `$1` für diesen Server:",
+        "channel_max": "Du hast zu viele Kanäle angegeben.",
+        "channel_missing": "Der angegebene Kanal existiert nicht.",
+        "current": "Dies sind die aktuellen Verifizierungen für diesen Server:",
+        "current_selected": "Dies ist die Verifizierung `$1` für diesen Server:",
         "dashboard": {
             "added": "$1 hat die Verifizierung mit der ID `$2` hinzugefügt.",
             "added_notice": "$1 hat einige Verifizierungshinweise hinzugefügt.",
@@ -751,7 +754,7 @@
             "updated_notice": "$1 hat einige Verifizierungshinweise aktualisiert."
         },
         "delete_current": "Lösche diese Verifizierung:",
-        "deleted": "die Verifizierung wurde gelöscht.",
+        "deleted": "Die Verifizierung wurde gelöscht.",
         "disabled": "deaktiviert",
         "editcount": "Bearbeitungen:",
         "enabled": "aktiviert",
@@ -760,15 +763,15 @@
         "indays": "(in Tagen)",
         "logging": "Protokollierungskanal:",
         "match": "Hinweis bei fehlenden Voraussetzungen:",
-        "max_entries": "du hast bereits die maximale Anzahl an Verifizierungen erreicht.",
-        "missing": "es sind noch keine Verifizierungen für diesen Server vorhanden.",
+        "max_entries": "Du hast bereits die maximale Anzahl an Verifizierungen erreicht.",
+        "missing": "Es sind noch keine Verifizierungen für diesen Server vorhanden.",
         "new_accountage": "<neues Account-Alter>",
         "new_channel": "<neuer Kanal>",
         "new_editcount": "<neues Bearbeitungslimit>",
         "new_postcount": "<neues Diskussionsbeitragslimit>",
         "new_role": "<neue Rolle>",
         "new_usergroup": "<neue Benutzergruppe>",
-        "no_role": "bitte gib eine Rolle für die neue Verifizierung an.",
+        "no_role": "Bitte gib eine Rolle für die neue Verifizierung an.",
         "notice_embed": "Einige Hinweise enthalten maskierte Markdown-Links. Stelle sicher das der Bot die `Links einbetten`-Berechtigung in allen Verifizierungskanälen besitzt damit diese richtig funktionieren.",
         "or": "oder",
         "postcount": "Diskussionsbeitragslimit (nur für Fandom Wikis):",
@@ -778,20 +781,20 @@
         "rename_no_permission": "**$1 fehlt die `Nicknames verwalten` Berechtigung um Wiki-Benutzernamen zu erzwingen!**",
         "role_add": "Rolle zum Vergeben:",
         "role_deleted": "**Die Rolle $1 scheint nicht mehr zu existieren!**",
-        "role_managed": "die angegebe Rolle kann nicht vergeben werden.",
-        "role_max": "du hast zu viele Rollen angegeben.",
-        "role_missing": "die angegebe Rolle existiert nicht.",
+        "role_managed": "Die angegebene Rolle kann nicht vergeben werden.",
+        "role_max": "Du hast zu viele Rollen angegeben.",
+        "role_missing": "Die angegebene Rolle existiert nicht.",
         "role_none": "keine",
         "role_remove": "Rolle zum Entfernen:",
         "role_too_high": "**Die Rolle $1 ist zu hoch für $2 um sie zu vergeben!**",
-        "save_failed": "die Verifizierungen konnten leider nicht gespeichert werden, bitte versuche es später erneut.",
+        "save_failed": "Die Verifizierungen konnten leider nicht gespeichert werden, bitte versuche es später erneut.",
         "success": "Hinweis bei Erfolg:",
         "toggle": "(umschalten)",
-        "updated": "die Verifizierung wurde aktualisiert:",
+        "updated": "Die Verifizierung wurde aktualisiert:",
         "usergroup": "Benutzergruppe:",
-        "usergroup_max": "du hast zu viele Benutzergruppen angegeben.",
-        "usergroup_too_long": "die angegebene Benutzergruppe ist zu lang.",
-        "value_too_high": "der angegebe Wert ist zu groß."
+        "usergroup_max": "Du hast zu viele Benutzergruppen angegeben.",
+        "usergroup_too_long": "Die angegebene Benutzergruppe ist zu lang.",
+        "value_too_high": "Der angegebene Wert ist zu groß."
     },
     "verify": {
         "audit_reason": "Verifziert als „$1“",
@@ -800,7 +803,7 @@
         "discord": "{{GENDER:$1|Discord-Benutzer|Discord-Benutzerin}}:",
         "empty": "*leer*",
         "error": "Die Verifizierung schlug fehl aufgrund eines Fehlers.",
-        "error_reply": "die Verifizierung schlug fehl aufgrund eines Fehlers, bitte versuche es erneut.",
+        "error_reply": "Die Verifizierung schlug fehl aufgrund eines Fehlers, bitte versuche es erneut.",
         "failed_gblock": "**Test auf globale Sperre schlug fehl!**",
         "failed_rename": "**Ändern {{GENDER:$1|seines|ihres|des}} Discord-Nickname schlug fehl!**",
         "failed_roles": "**Hinzufügen von Rollen schlug fehl!**",
@@ -810,12 +813,12 @@
         "help_guide": "Folge [dieser Anleitung]($1) um dein Discord-Tag auf deinem Wiki-Profil hinzuzufügen:",
         "help_missing": "Bitte stelle sicher, dass du deinen Wiki-Benutzernamen verwendest und die Groß- und Kleinschreibung stimmt.",
         "help_subpage": "Bitte füge dein Discord-Tag ($1) zu deiner Discord-Unterseite im Wiki hinzu:",
-        "missing": "es sind keine Verifizierungen für diesen Kanal eingestellt.",
+        "missing": "Es sind keine Verifizierungen für diesen Kanal eingestellt.",
         "notice": "Hinweis:",
         "oauth_button": "Authentifizieren",
-        "oauth_message": "bitte nutze [diesen Link]($1) um deinen Wiki-Account zu authentifizieren.",
+        "oauth_message": "Bitte nutze [diesen Link]($1) um deinen Wiki-Account zu authentifizieren.",
         "oauth_message_dm": "Bitte nutze diesen Link um deinen Wiki-Account für $1 zu authentifizieren.",
-        "oauth_private": "dieses Wiki nutzt OAuth2 zur Verifizierung. Bitte erlaube Direktnachrichten von diesem Server oder nutze den `/verify`-Befehl damit ich dir privat einen Authentifizierungslink senden kann.",
+        "oauth_private": "Dieses Wiki nutzt OAuth2 zur Verifizierung. Bitte erlaube Direktnachrichten von diesem Server oder nutze den `/verify`-Befehl damit ich dir privat einen Authentifizierungslink senden kann.",
         "oauth_used": "*Verifiziert über OAuth2*",
         "qualified_add": "Hinzugefügt zu:",
         "qualified_add_error": "Kann nicht hinzugefügt werden zu:",
@@ -841,9 +844,9 @@
     "voice": {
         "channel": "Sprachkanal",
         "disable": "Nutze `$1` um diese Funktion zu deaktivieren.",
-        "disabled": "du hast die Funktion, Rollen für Sprachkanäle hinzuzufügen, deaktiviert.",
+        "disabled": "Du hast die Funktion, Rollen für Sprachkanäle hinzuzufügen, deaktiviert.",
         "enable": "Nutze `$1` um diese Funktion zu aktivieren.",
-        "enabled": "du hast die Funktion, Rollen für Sprachkanäle hinzuzufügen, aktiviert.",
+        "enabled": "Du hast die Funktion, Rollen für Sprachkanäle hinzuzufügen, aktiviert.",
         "join": "$1 hat den Sprachkanal „$2“ betreten.",
         "left": "$1 hat den Sprachkanal „$2“ verlassen.",
         "name": "Name des Sprachkanals",

+ 3 - 0
i18n/en.json

@@ -591,6 +591,8 @@
         "infosearch": "Not the correct result? Use $1 for a direct link or $2 for a list of all hits.",
         "loading": "Loading page description…",
         "media": "To the file description page",
+        "messagedefault": "Default:",
+        "messagedefaultnone": "*none*",
         "results": "$1 total {{PLURAL:$2|result|results}}",
         "special": "Content of this special page:"
     },
@@ -658,6 +660,7 @@
         "MediaWiki": "Requires at least $1 for full functionality, found `$2`.",
         "notice": "Limited functionality",
         "pause": "I'm currently paused on this server.",
+        "slow": "Sorry, I seem to be a bit slow right now.",
         "text": [
             "I'm fully functional!",
             "I'm still alive!",

+ 85 - 81
i18n/es.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "es-ES",
     "diff": {
-        "badrev": "¡al menos una edición no existe!",
+        "badrev": "¡Al menos una edición no existe!",
         "hidden": "*oculto*",
         "info": {
             "added": "Añadido:",
@@ -146,15 +146,15 @@
     ],
     "general": {
         "database": "⚠️ **Funcionalidad limitada** ⚠️\n¡No se encontraron configuraciones, por favor contacta al propietario del bot!",
-        "default": "este servidor aún no está configurado. Usa $1 o el panel de control para cambiar la configuración.",
+        "default": "Este servidor aún no está configurado. Usa $1 o el panel de control para cambiar la configuración.",
         "disclaimer": "Soy un pequeño bot creado con el propósito de vincular y buscar fácilmente sitios de MediaWiki como Wikipedia y wikis de Fandom. Muestro breves descripciones e información adicional sobre las páginas y puedo resolver redireccionamientos y seguir enlaces interwiki. $1 me escribió en JavaScript.\n\nPuedes apoyarme en Patreon:",
         "experimental": "**¡Esta función es experimental! No se garantiza que funcione correctamente y puede eliminarse en el futuro.**",
         "helpserver": "Si tienes preguntas o problemas, visita mi servidor de soporte:",
         "limit": "🚨 ** ¡Detente, has llegado al límite! ** 🚨\n\n$1, ¡tu mensaje contenía demasiados comandos!",
         "missingperm": "Me faltan algunos permisos para este comando:",
-        "patreon": "¡esta es una función exclusiva de Patreon!\nPuedes apoyarme en Patreon para acceder a esta función:",
-        "prefix": "el prefijo de este servidor es `$1`. Puedes cambiar el prefijo con `$1settings prefix`. Para obtener una lista de todos los comandos, consulta `$1help`.",
-        "readonly": "**la base de datos está actualmente en modo de solo lectura, ¡no puedes cambiar ninguna configuración en este momento!**"
+        "patreon": "¡Esta es una función exclusiva de Patreon!\nPuedes apoyarme en Patreon para acceder a esta función:",
+        "prefix": "El prefijo de este servidor es `$1`. Puedes cambiar el prefijo con `$1settings prefix`. Para obtener una lista de todos los comandos, consulta `$1help`.",
+        "readonly": "**La base de datos está actualmente en modo de solo lectura, ¡no puedes cambiar ninguna configuración en este momento!**"
     },
     "help": {
         "admin": "Estos comandos solo pueden ser ejecutados por administradores:",
@@ -389,7 +389,7 @@
                 "desc": "Responderé con un enlace a un artículo que coincida en el wiki de Wikia llamado: `https://<wiki>.wikia.org/`"
             }
         },
-        "noadmin": "¡necesitas el permiso `Manage Server` para estos comandos!",
+        "noadmin": "¡Necesitas el permiso `Manage Server` para estos comandos!",
         "pause": "**¡Actualmente estoy en pausa en este servidor!**\nSolo se pueden ejecutar estos comandos:"
     },
     "interaction": {
@@ -491,33 +491,33 @@
         "ad": "¿Quieres cambios recientes directamente en Discord? ¡Usa `$1rcscript` para agregar un webhook de cambios recientes basado en **$2** a tu servidor de Discord!",
         "add_more": "Agrega más webhooks de cambios recientes:",
         "added": "Se ha agregado un webhook de cambios recientes a:",
-        "all_inactive": "no puedes deshabilitar los cambios al wiki y los cambios basados en feeds al mismo tiempo.",
+        "all_inactive": "No puedes deshabilitar los cambios al wiki y los cambios basados en feeds al mismo tiempo.",
         "audit_reason": "Webhook de cambios recientes para \"$1\"",
         "audit_reason_delete": "Webhook de cambios recientes eliminado",
         "audit_reason_edit": "Actualizó el webhook de cambios recientes",
         "avatar": "Avatar del webhook:",
-        "blocked": "¡este wiki ha sido bloqueado para que no se agregue como un webhook de cambios recientes!",
-        "blocked_reason": "¡este wiki ha sido bloqueado para que no se agregue como un webhook de cambios recientes por `$1`!",
+        "blocked": "¡Este wiki ha sido bloqueado para que no se agregue como un webhook de cambios recientes!",
+        "blocked_reason": "¡Este wiki ha sido bloqueado para que no se agregue como un webhook de cambios recientes por `$1`!",
         "channel": "Canal:",
-        "current": "estos son los webhooks de cambios recientes de este servidor:",
-        "current_display": "el modo de visualización de este webhook es:",
-        "current_lang": "el idioma de este webhook es:",
-        "current_selected": "este es el webhook de cambios recientes `$1` para este servidor:",
-        "current_wiki": "el wiki de este webhook es:",
+        "current": "Estos son los webhooks de cambios recientes de este servidor:",
+        "current_display": "El modo de visualización de este webhook es:",
+        "current_lang": "El idioma de este webhook es:",
+        "current_selected": "Este es el webhook de cambios recientes `$1` para este servidor:",
+        "current_wiki": "El wiki de este webhook es:",
         "dashboard": {
             "added": "$1 agregó el webhook de cambios recientes con el ID `$2`.",
             "removed": "$1 eliminó el webhook de cambios recientes con el ID `$2`.",
             "updated": "$1 actualizó el webhook de cambios recientes con el ID `$2`."
         },
         "delete": "Elimina este webhook de cambios recientes:",
-        "deleted": "se ha eliminado el webhook de cambios recientes.",
+        "deleted": "Se ha eliminado el webhook de cambios recientes.",
         "disabled": "desactivado",
         "disabled_feeds": "Los cambios basados en feeds, como discusiones, muros de mensajes y comentarios de artículos para este webhook se han desactivado.",
-        "disabled_rc": "se han desactivado los cambios del wiki para este webhook.",
+        "disabled_rc": "Se han desactivado los cambios del wiki para este webhook.",
         "display": "Modo de visualización:",
         "enabled": "activado",
-        "enabled_feeds": "se han activado los cambios basados en feeds, como discusiones, muros de mensajes y comentarios de artículos para este webhook.",
-        "enabled_rc": "se han activado los cambios del wiki para este webhook.",
+        "enabled_feeds": "Se han activado los cambios basados en feeds, como discusiones, muros de mensajes y comentarios de artículos para este webhook.",
+        "enabled_rc": "Se han activado los cambios del wiki para este webhook.",
         "feeds": "Cambios basados en feeds:",
         "help_display_compact": "Mensajes de texto compactos con enlaces en la misma línea.",
         "help_display_diff": "Mensajes adjuntos con vistas previas de imágenes y diferencias entre ediciones.",
@@ -527,20 +527,20 @@
         "help_lang": "Los idiomas disponibles actualmente son:",
         "help_wiki": "Enlace a un sitio de MediaWiki como `https://<wiki>.fandom.com/`",
         "lang": "Idioma:",
-        "max_entries": "ya alcanzaste la cantidad máxima de webhooks de cambios recientes.",
-        "missing": "todavía no hay webhooks de cambios recientes en este servidor.",
+        "max_entries": "Ya alcanzaste la cantidad máxima de webhooks de cambios recientes.",
+        "missing": "Todavía no hay webhooks de cambios recientes en este servidor.",
         "name": "Nombre del webhook:",
         "new_lang": "<nuevo idioma>",
         "new_wiki": "<enlace al wiki>",
-        "no_feeds": "el wiki de este webhook no tiene funciones basadas en feeds, como discusiones, muros de mensajes o comentarios de artículos, habilitadas.",
-        "noadmin": "¡necesitas el permiso `Manage Webhooks` para ejecutar este comando!",
+        "no_feeds": "El wiki de este webhook no tiene funciones basadas en feeds, como discusiones, muros de mensajes o comentarios de artículos, habilitadas.",
+        "noadmin": "¡Necesitas el permiso `Manage Webhooks` para ejecutar este comando!",
         "rc": "Cambios del wiki:",
-        "sysmessage": "el mensaje del sistema `$1` tiene que ser el ID del servidor `$2` para agregar un webhook de cambios recientes.",
+        "sysmessage": "El mensaje del sistema `$1` tiene que ser el ID del servidor `$2` para agregar un webhook de cambios recientes.",
         "title": "Webhook de cambios recientes",
         "toggle": "(activar/desactivar)",
-        "updated_display": "el modo de visualización de este webhook se ha cambiado a:",
-        "updated_lang": "el idioma de este webhook se ha cambiado a:",
-        "updated_wiki": "el wiki de este webhook se ha cambiado a:",
+        "updated_display": "El modo de visualización de este webhook se ha cambiado a:",
+        "updated_lang": "El idioma de este webhook se ha cambiado a:",
+        "updated_wiki": "El wiki de este webhook se ha cambiado a:",
         "webhook": {
             "blocked": "¡Este webhook de cambios recientes se eliminará porque el wiki ha sido bloqueado!",
             "blocked_help": "Puedes solicitar más detalles en el [servidor de soporte]($1).",
@@ -574,10 +574,11 @@
             "updated_lang": "El idioma se ha cambiado a `$1` en este webhook de cambios recientes.",
             "updated_wiki": "El wiki se ha cambiado a $1 en este webhook de cambios recientes."
         },
-        "webhook_failed": "lamentablemente, no se pudo crear el webhook. Vuelve a intentarlo más tarde.",
+        "webhook_failed": "Lamentablemente, no se pudo crear el webhook. Vuelve a intentarlo más tarde.",
         "wiki": "Wiki:"
     },
     "search": {
+        "cached": "Caché actualizado por última vez:",
         "category": {
             "content": "Contenido de esta categoría:",
             "empty": "*Esta categoría está vacía*",
@@ -590,19 +591,21 @@
         "infosearch": "¿No es el resultado correcto? Usa $1 para obtener un enlace directo o $2 para obtener una lista de todos los resultados.",
         "loading": "Cargando descripción de la página…",
         "media": "A la página de descripción del archivo",
+        "messagedefault": "Predeterminado:",
+        "messagedefaultnone": "*ninguno*",
         "results": "$1 total {{PLURAL:$2|resultado|resultados}}",
         "special": "Contenido de esta página especial:"
     },
     "settings": {
         "button": "Usar el panel de control",
-        "channel current": "estas son las configuraciones actuales en este canal:",
-        "channel lang": "el idioma de este canal es:",
-        "channel langchanged": "cambiaste el idioma de este canal a:",
-        "channel role": "el rol mínimo requerido para este canal es:",
-        "channel rolechanged": "cambiaste el rol mínimo requerido en este canal a:",
-        "channel wiki": "el wiki predeterminado en este canal es:",
-        "channel wikichanged": "cambiaste el wiki predeterminado en este canal a:",
-        "current": "estas son las configuraciones actuales en este servidor:",
+        "channel current": "Estas son las configuraciones actuales en este canal:",
+        "channel lang": "El idioma de este canal es:",
+        "channel langchanged": "Cambiaste el idioma de este canal a:",
+        "channel role": "El rol mínimo requerido para este canal es:",
+        "channel rolechanged": "Cambiaste el rol mínimo requerido en este canal a:",
+        "channel wiki": "El wiki predeterminado en este canal es:",
+        "channel wikichanged": "Cambiaste el wiki predeterminado en este canal a:",
+        "current": "Estas son las configuraciones actuales en este servidor:",
         "currentchannel": "Sobrescrituras de este canal:",
         "currentinline": "Comandos inline:",
         "currentlang": "Idioma:",
@@ -616,51 +619,52 @@
         },
         "foundwikis": "¿Te refieres a alguno de estos wikis?",
         "inline disabled": {
-            "channel inline": "los comandos inline están actualmente deshabilitados en este canal.",
-            "channel inlinechanged": "deshabilitaste los comandos inline en este canal.",
+            "channel inline": "Los comandos inline están actualmente deshabilitados en este canal.",
+            "channel inlinechanged": "Deshabilitaste los comandos inline en este canal.",
             "help": "Utiliza `$1` para habilitar comandos inline como `[[$2]]` y `{{$2}}`.",
-            "inline": "los comandos inline están actualmente deshabilitados en este servidor.",
-            "inlinechanged": "deshabilitaste los comandos inline en este servidor."
+            "inline": "Los comandos inline están actualmente deshabilitados en este servidor.",
+            "inlinechanged": "Deshabilitaste los comandos inline en este servidor."
         },
         "inline enabled": {
-            "channel inline": "los comandos inline están actualmente habilitados en este canal.",
-            "channel inlinechanged": "habilitaste los comandos inline en este canal.",
+            "channel inline": "Los comandos inline están actualmente habilitados en este canal.",
+            "channel inlinechanged": "Habilitaste los comandos inline en este canal.",
             "help": "Use `$1` para deshabilitar comandos inline como `[[$2]]` y `{{$2}}`.",
-            "inline": "los comandos inline están actualmente habilitados en este servidor.",
-            "inlinechanged": "habilitaste los comandos inline en este servidor."
+            "inline": "Los comandos inline están actualmente habilitados en este servidor.",
+            "inlinechanged": "Habilitaste los comandos inline en este servidor."
         },
-        "lang": "el idioma de este servidor es:",
-        "langchanged": "has cambiado el idioma de este servidor a:",
+        "lang": "El idioma de este servidor es:",
+        "langchanged": "Has cambiado el idioma de este servidor a:",
         "langhelp": "Utiliza `$1 <language>` para cambiar el idioma.\nLos idiomas disponibles actualmente son:",
-        "langinvalid": "¡el idioma especificado no está disponible!",
-        "missing": "este servidor aún no está configurado. Usa $1 y $2 para cambiar la configuración.",
+        "langinvalid": "¡El idioma especificado no está disponible!",
+        "missing": "Este servidor aún no está configurado. Usa $1 y $2 para cambiar la configuración.",
         "nochannels": "*No hay sobrescrituras de canal todavía*",
-        "prefix": "el prefijo de este servidor es:",
-        "prefixchanged": "cambiaste el prefijo de este servidor a:",
+        "prefix": "El prefijo de este servidor es:",
+        "prefixchanged": "Cambiaste el prefijo de este servidor a:",
         "prefixhelp": "Usa `$1 <prefix>` para cambiar el prefijo.\nUsa `_` al final para indicar un espacio al final del prefijo.\n¡El prefijo no puede incluir menciones!",
-        "prefixinvalid": "¡el prefijo especificado no es compatible!",
-        "role": "el rol mínimo requerido para este servidor es:",
-        "rolechanged": "el rol mínimo requerido en este servidor es:",
+        "prefixinvalid": "¡El prefijo especificado no es compatible!",
+        "role": "El rol mínimo requerido para este servidor es:",
+        "rolechanged": "El rol mínimo requerido en este servidor es:",
         "rolehelp": "Usa `$1 <rol>` para cambiar el rol mínimo requerido.",
-        "roleinvalid": "¡el rol especificado no existe!",
-        "save_failed": "lamentablemente, la configuración no se pudo guardar, intenta nuevamente más tarde.",
-        "wiki": "el wiki predeterminado de este servidor es:",
-        "wikichanged": "cambiaste el wiki predeterminado de este servidor a:",
+        "roleinvalid": "¡El rol especificado no existe!",
+        "save_failed": "Lamentablemente, la configuración no se pudo guardar, intenta nuevamente más tarde.",
+        "wiki": "El wiki predeterminado de este servidor es:",
+        "wikichanged": "Cambiaste el wiki predeterminado de este servidor a:",
         "wikihelp": "Usa `$1 <link> para cambiar el wiki predeterminado.\nY el enlace un sitio de MediaWiki como `https://<wiki>.fandom.com/`",
         "wikiinvalid": "¡Proporciona un enlace válido a un sitio de MediaWiki, como Wikipedia o un wiki de Fandom!",
         "wikiinvalid_http": "¡El sitio web proporcionado no tiene un certificado TLS/SSL válido! Por razones de seguridad, solo se admiten wikis que utilizan HTTPS.\nSi eres el administrador del sitio, puedes obtener un certificado de una autoridad de certificación como *Let’s Encrypt*:\n<https://letsencrypt.org/getting-started/>",
         "wikiinvalid_private": "¡El wiki proporcionado es privado! Solo se admiten wikis públicos que todos puedan leer.",
         "wikiinvalid_timeout": "¡El enlace proporcionado tardó demasiado en responder!",
-        "wikimissing": "¡todavía no hay un wiki predeterminado en este servidor!"
+        "wikimissing": "¡Todavía no hay un wiki predeterminado en este servidor!"
     },
     "test": {
         "MediaWiki": "Requiere al menos $1 para la funcionalidad completa, se encuentra la versión `$2`.",
         "notice": "Funcionalidad limitada",
         "pause": "Actualmente estoy en pausa en este servidor.",
+        "slow": "Lo siento, estoy un poco lento en este momento.",
         "text": [
             "¡Estoy completamente funcional!",
             " ",
-            " ",
+            " Y créeme, todavía estoy vivo.",
             " ",
             " ",
             " ",
@@ -735,13 +739,13 @@
     "verification": {
         "accountage": "Antigüedad de la cuenta:",
         "add_more": "Agregar más verificaciones:",
-        "added": "se ha agregado la verificación:",
+        "added": "Se ha agregado la verificación:",
         "and": "y",
         "channel": "Canal:",
-        "channel_max": "proporcionaste demasiados canales.",
-        "channel_missing": "el canal proporcionado no existe.",
-        "current": "estas son las verificaciones actuales en este servidor:",
-        "current_selected": "esta es la verificación `$1` en este servidor:",
+        "channel_max": "Proporcionaste demasiados canales.",
+        "channel_missing": "El canal proporcionado no existe.",
+        "current": "Estas son las verificaciones actuales en este servidor:",
+        "current_selected": "Esta es la verificación `$1` en este servidor:",
         "dashboard": {
             "added": "$1 agregó la verificación con ID `$2`.",
             "added_notice": "$1 agregó algunos avisos de verificación.",
@@ -750,7 +754,7 @@
             "updated_notice": "$1 actualizó algunos avisos de verificación."
         },
         "delete_current": "Eliminar esta verificación:",
-        "deleted": "la verificación ha sido eliminada.",
+        "deleted": "La verificación ha sido eliminada.",
         "disabled": "desactivada",
         "editcount": "Número de ediciones:",
         "enabled": "activada",
@@ -759,15 +763,15 @@
         "indays": "(en días)",
         "logging": "Canal de registro:",
         "match": "Aviso de requisitos faltantes:",
-        "max_entries": "ya alcanzaste la cantidad máxima de verificaciones.",
-        "missing": "todavía no hay verificaciones en este servidor.",
+        "max_entries": "Ya alcanzaste la cantidad máxima de verificaciones.",
+        "missing": "Todavía no hay verificaciones en este servidor.",
         "new_accountage": "<nueva antigüedad de la cuenta>",
         "new_channel": "<nuevo canal>",
         "new_editcount": "<nuevo número de ediciones>",
         "new_postcount": "<nuevo número de publicaciones>",
         "new_role": "<nuevo rol>",
         "new_usergroup": "<nuevo grupo de usuarios>",
-        "no_role": "por favor proporciona un rol para la nueva verificación.",
+        "no_role": "Por favor proporciona un rol para la nueva verificación.",
         "notice_embed": "Algunos avisos incluyen enlaces enmascarado en Markdown. Asegúrate de que el bot tenga los permisos `Insertar enlaces` en todos los canales de verificación para que funcionen correctamente.",
         "or": "o",
         "postcount": "Número de publicaciones (solo wikis de Fandom):",
@@ -777,20 +781,20 @@
         "rename_no_permission": "**¡A $1 le falta el permiso `Manage Nicknames` para imponer nombres de usuario wiki!**",
         "role_add": "Rol a agregar:",
         "role_deleted": "**¡El rol $1 parece que ya no existe!**",
-        "role_managed": "no se puede asignar el rol proporcionado.",
-        "role_max": "proporcionaste demasiados roles.",
-        "role_missing": "el rol proporcionado no existe.",
+        "role_managed": "No se puede asignar el rol proporcionado.",
+        "role_max": "Proporcionaste demasiados roles.",
+        "role_missing": "El rol proporcionado no existe.",
         "role_none": "ninguno",
         "role_remove": "Rol a remover:",
         "role_too_high": "**¡El rol $1 está en un rango muy alto como para asignarlo a $2!**",
-        "save_failed": "lamentablemente, no se pudo guardar la verificación. Vuelve a intentarlo más tarde.",
+        "save_failed": "Lamentablemente, no se pudo guardar la verificación. Vuelve a intentarlo más tarde.",
         "success": "Aviso de operación correcta:",
         "toggle": "(activar/desactivar)",
-        "updated": "la verificación se ha actualizado:",
+        "updated": "La verificación se ha actualizado:",
         "usergroup": "Grupo de usuario:",
-        "usergroup_max": "proporcionaste demasiados grupos de usuarios.",
-        "usergroup_too_long": "el grupo de usuarios proporcionado es demasiado largo.",
-        "value_too_high": "el valor proporcionado es demasiado alto."
+        "usergroup_max": "Proporcionaste demasiados grupos de usuarios.",
+        "usergroup_too_long": "El grupo de usuarios proporcionado es demasiado largo.",
+        "value_too_high": "El valor proporcionado es demasiado alto."
     },
     "verify": {
         "audit_reason": "Verificado como \"$1\"",
@@ -799,7 +803,7 @@
         "discord": "Usuario de Discord:",
         "empty": "*vacío*",
         "error": "La verificación falló debido a un error.",
-        "error_reply": "la verificación falló debido a un error, inténtalo de nuevo.",
+        "error_reply": "La verificación falló debido a un error, inténtalo de nuevo.",
         "failed_gblock": "**¡La verificación de bloqueo global falló!**",
         "failed_rename": "**¡Ha fallado el cambio de {{SEXO:$1|su|su|su}} alias de Discord!**",
         "failed_roles": "**¡Error al agregar roles!**",
@@ -809,12 +813,12 @@
         "help_guide": "Sigue [esta guía]($1) para agregar tu etiqueta de Discord a tu perfil wiki:",
         "help_missing": "Asegúrate de utilizar tu nombre de usuario wiki y que las mayúsculas y minúsculas son correctas.",
         "help_subpage": "Por favor agrega tu etiqueta de Discord ($1) a tu subpágina de Discord en el wiki:",
-        "missing": "no hay verificaciones configuradas en este canal.",
+        "missing": "No hay verificaciones configuradas en este canal.",
         "notice": "Aviso:",
         "oauth_button": "Autenticar",
-        "oauth_message": "por favor usa [este enlace]($1) para autenticar tu cuenta wiki.",
+        "oauth_message": "Por favor usa [este enlace]($1) para autenticar tu cuenta wiki.",
         "oauth_message_dm": "Por favor, utiliza este enlace para autenticar tu cuenta wiki para $1.",
-        "oauth_private": "el wiki usa OAuth2 para la verificación. Por favor, habilita los mensajes directos de este servidor o use el comando `/verify` para que pueda enviarte un enlace de autenticación de forma privada.",
+        "oauth_private": "El wiki usa OAuth2 para la verificación. Por favor, habilita los mensajes directos de este servidor o use el comando `/verify` para que pueda enviarte un enlace de autenticación de forma privada.",
         "oauth_used": "*Verificado usando OAuth2*",
         "qualified_add": "Agregado a:",
         "qualified_add_error": "No se puede agregar a:",
@@ -840,9 +844,9 @@
     "voice": {
         "channel": "Canal de chat de voz",
         "disable": "Usa `$1` para desactivar esta función.",
-        "disabled": "desactivaste la función de agregar roles para canales de chat de voz.",
+        "disabled": "Desactivaste la función de agregar roles para canales de chat de voz.",
         "enable": "Usa `$1` para activar esta función.",
-        "enabled": "activaste la función de agregar roles para canales de chat de voz.",
+        "enabled": "Activaste la función de agregar roles para canales de chat de voz.",
         "join": "$1 se unió al canal de chat de voz \"$2\".",
         "left": "$1 salió del canal de chat de voz \"$2\".",
         "name": "nombre del canal de chat de voz",

+ 18 - 17
i18n/hi.json

@@ -497,13 +497,13 @@
         "audit_reason_edit": "रीसेंट चेंजेस वेबहुक को अपडेट किया गया",
         "avatar": "वेबहुक का अवतार:",
         "blocked": "इस विकी को रीसेंट चेंजेस वेबहुक बनाने से ब्लॉक कर दिया गया है!",
-        "blocked_reason": "इस विक को `$1` के लिए रीसेंट चेंजेस वेबहुक बनाने से ब्लॉक कर दिया गया है!",
+        "blocked_reason": "इस विकि को `$1` के लिए रीसेंट चेंजेस वेबहुक बनाने से ब्लॉक कर दिया गया है!",
         "channel": "चैनल:",
         "current": "इस सर्वर पर ये वर्तमान रीसेंट चेंजेस वेबहुक हैं:",
         "current_display": "इस वेबहुक का डिस्प्ले मोड है:",
         "current_lang": "इस वेबहुक की भाषा है:",
         "current_selected": "यह इस सर्वर का वर्तमान रीसेंट चेंजेस वेबहुक `$1` है:",
-        "current_wiki": "वेबहुक का विक है:",
+        "current_wiki": "वेबहुक का विकि है:",
         "dashboard": {
             "added": "$1 ने ID `$2` के साथ रीसेंट चेंजेस वेबहुक जोड़ा है।",
             "removed": "$1 ने ID `$2` के साथ रीसेंट चेंजेस वेबहुक हटाया है।",
@@ -512,12 +512,12 @@
         "delete": "इस रीसेंट चेंजेस वेबहुक को डिलीट करें:",
         "deleted": "रीसेंट चेंजेस वेबहुक को डिलीट कर दिया गया है।",
         "disabled": "अक्षम",
-        "disabled_feeds": "फीड-आधारित बदलाव, जैसे डिसकशंस, मैसेज वॉल और आर्टिकल कमेंट, इस वेबहुक के लिए अक्षम हैं।",
-        "disabled_rc": "इस वेबहुक के लिए विक के बदलाव अक्षम हैं।",
+        "disabled_feeds": "फीड-आधारित बदलाव, जैसे चर्चा, मैसेज वॉल और आर्टिकल कमेंट, इस वेबहुक के लिए अक्षम हैं।",
+        "disabled_rc": "इस वेबहुक के लिए विकि के बदलाव अक्षम हैं।",
         "display": "डिस्प्ले मोड:",
         "enabled": "सक्षम",
-        "enabled_feeds": "फीड-आधारित बदलाव, जैसे डिसकशंस, मैसेज वॉल और आर्टिकल कमेंट, इस वेबहुक के लिए सक्षम कर दिए गए हैं।",
-        "enabled_rc": "इस वेबहुक के लिए विक के बदलाव सक्षम कर दिए गए हैं।",
+        "enabled_feeds": "फीड-आधारित बदलाव, जैसे चर्चा, मैसेज वॉल और आर्टिकल कमेंट, इस वेबहुक के लिए सक्षम कर दिए गए हैं।",
+        "enabled_rc": "इस वेबहुक के लिए विकि के बदलाव सक्षम कर दिए गए हैं।",
         "feeds": "फीड-आधारित बदलाव:",
         "help_display_compact": "इनलाइन लिंकों वाला कॉम्पैक्ट टेक्स्ट।",
         "help_display_diff": "चित्र पूर्वावलोकन और सम्पादना बदलावों के साथ मैसेजों को एम्बेड करें।",
@@ -532,7 +532,7 @@
         "name": "वेबहुक का नाम:",
         "new_lang": "<नई भाषा>",
         "new_wiki": "<विकी का लिंक>",
-        "no_feeds": "इस वेबहुक के विकी पर कोई फीड-आधारित बदलाव, जैसे डिसकशंस, मैसेज वॉल और आर्टिकल कमेंट, सक्षम नहीं है।",
+        "no_feeds": "इस वेबहुक के विकि पर कोई फीड-आधारित बदलाव, जैसे चर्चा, मैसेज वॉल और आर्टिकल कमेंट, सक्षम नहीं है।",
         "noadmin": "इस कमांड का इस्तेमाल करने के लिए आपको `Manage Webhooks` अनुमति चाहिए!",
         "rc": "विकी के बदलाव:",
         "sysmessage": "रीसेंट चेंजेस वेबहुक जोड़ने के लिए सिस्टम मैसेज `$1` को सर्वर ID `$2` होना होगा।",
@@ -540,7 +540,7 @@
         "toggle": "(टॉगल)",
         "updated_display": "इस वेबहुक के डिस्प्ले मोड को इस में बदल दिया गया है:",
         "updated_lang": "इस वेबहुक के भाषा को इस में बदल दिया गया है:",
-        "updated_wiki": "इस वेबहुक के विक को इस में बदल दिया गया है:",
+        "updated_wiki": "इस वेबहुक के विकि को इस में बदल दिया गया है:",
         "webhook": {
             "blocked": "इस रीसेंट चेंजेस वेबहुक को डिलीट कर दिया जाएगा क्योंकि विकी को ब्लॉक कर दिया गया है!",
             "blocked_help": "आप [सहायता सर्वर]($1) पर और जानकारी पा सकते हैं।",
@@ -578,6 +578,7 @@
         "wiki": "विकी:"
     },
     "search": {
+        "cached": "आखिरी कैश अपडेट:",
         "category": {
             "content": "इस श्रेणी का कंटेंट:",
             "empty": "*यह श्रेणी खाली है*",
@@ -600,8 +601,8 @@
         "channel langchanged": "आपने इस चैनल के भाषा को इस में बदला:",
         "channel role": "इस चैनल का न्यूनतम ज़रूरी रोल है:",
         "channel rolechanged": "आपने इस चैनल के न्यूनतम ज़रूरी रोल को इस में बदला:",
-        "channel wiki": "इस चैनल का डिफ़ॉल्ट विक है:",
-        "channel wikichanged": "आपने इस चैनल के डिफ़ॉल्ट विक को इस में बदला:",
+        "channel wiki": "इस चैनल का डिफ़ॉल्ट विकि है:",
+        "channel wikichanged": "आपने इस चैनल के डिफ़ॉल्ट विकि को इस में बदला:",
         "current": "ये इस सर्वर के वर्तमान सेटिंग्स हैं:",
         "currentchannel": "चैनल ओवर्राइट्स:",
         "currentinline": "इनलाइन कमांड:",
@@ -644,14 +645,14 @@
         "rolehelp": "न्यूनतम ज़रूरी रोल को बदलने के लिए `$1<रोल>` का इस्तेमाल करें।",
         "roleinvalid": "दिया गया रोल मौजूद नहीं है!",
         "save_failed": "बदकिस्मती से सेटिंग्स सेव नहीं हो पाए। कृपया थोड़ी देर बाद कोशिश करें।",
-        "wiki": "इस सर्वर का डिफ़ॉल्ट विक है:",
-        "wikichanged": "आपने इस सर्वर के डिफ़ॉल्ट विक को इस में बदला:",
+        "wiki": "इस सर्वर का डिफ़ॉल्ट विकि है:",
+        "wikichanged": "आपने इस सर्वर के डिफ़ॉल्ट विकि को इस में बदला:",
         "wikihelp": "डिफ़ॉल्ट विकी बदलने के लिए $1 <लिंक>` का इस्तेमाल करें।\n`https://<विकि>.fandom.com/` जैसे मीडियाविकी साइट तक लिंक करें",
-        "wikiinvalid": "कृपया फैनडम विकि या विकिपीडिया जैसे स्वीकृत मीडियाविकि साइट के लिंक दें!",
-        "wikiinvalid_http": "इस वेबसाइट के पास कोई स्वीकृत TLS/SSL सर्टिफिकेट नहीं है! सुरक्षा के कारणों से सिर्फ HTTPS का इस्तेमाल करने वाले विकियाँ ही समर्थित हैं।\nअगर आप साइट के प्रबंधक हैं, *Let’s Encrypt* जैसे किसी सर्टिफिकेट प्राधिकारी से सर्टिफिकेट पा सकते हैं।\n<https://letsencrypt.org/getting-started/>",
+        "wikiinvalid": "कृपया किसी फैनडम विकि या विकिपीडिया जैसे स्वीकृत मीडियाविकि साइट के लिंक दें!",
+        "wikiinvalid_http": "इस वेबसाइट के पास कोई स्वीकृत TLS/SSL प्रमाणपत्र नहीं है! सुरक्षा के कारणों से सिर्फ HTTPS का इस्तेमाल करने वाले विकियाँ ही समर्थित हैं।\nअगर आप साइट के प्रबंधक हैं, *Let’s Encrypt* जैसे किसी प्रमाणपत्र प्राधिकारी से प्रमाणपत्र पा सकते हैं।\n<https://letsencrypt.org/getting-started/>",
         "wikiinvalid_private": "यह विकि व्यक्तिगत हैं! सिर्फ सार्वजनिक विकियाँ ही समर्थित हैं जिन्हें सब पढ़ सकते हैं।",
         "wikiinvalid_timeout": "इस लिंक को जवाब देने में कुछ ज़्यादा ही वक्त लग गया!",
-        "wikimissing": "इस सर्वर के लिए कोई डिफ़ॉल्ट विक नहीं है!"
+        "wikimissing": "इस सर्वर के लिए कोई डिफ़ॉल्ट विकि नहीं है!"
     },
     "test": {
         "MediaWiki": "पूरे काम के लिए कम-से-कम $1 चाहिए, `$2` मिला।",
@@ -660,7 +661,7 @@
         "text": [
             "मैं अभी भी काम करता हूँ!",
             "मैं ज़िंदा हूँ, जी!",
-            "मानो या न मानो, मैं ज़िंदा हूँ।",
+            "मानो या न मानो, मैं ज़िंदा हूँ।",
             "मैंने विज्ञान पढ़ा था और मैं अभी भी ज़िंदा हूँ।",
             "मैं ज़िंदा हूँ और यह ज़िन्दगी कितनी सुहानी है।",
             "हाँ जी, काम तो मैं अभी भी करता हूँ।",
@@ -799,7 +800,7 @@
         "discord": "डिस्कॉर्ड सदस्य:",
         "empty": "*खाली*",
         "error": "एक गलती के चलते वेरिफिकेशन असफल रहा।",
-        "error_reply": "क गलती के चलते वेरिफिकेशन असफल रहा। कृपया थोड़ी देर बाद कोशिश करें।",
+        "error_reply": "किसी गलती के चलते वेरिफिकेशन असफल रहा। कृपया थोड़ी देर बाद कोशिश करें।",
         "failed_gblock": "**ग्लोबल ब्लॉक की जाँच नाकामयाब रही!**",
         "failed_rename": "**उसके डिस्कॉर्ड निकनेम को बदला न जा सका!**",
         "failed_roles": "**रोल जोड़ा न जा सका!**",

+ 507 - 68
i18n/it.json

@@ -1,7 +1,7 @@
 {
     "__translator": [
         "Raffox97",
-        " ",
+        "Daeron",
         " ",
         " ",
         " ",
@@ -13,98 +13,98 @@
     ],
     "aliases": {
         "bug": [
-            " ",
+            "errore",
             " ",
             " ",
             " ",
             " "
         ],
         "command": [
-            "",
+            "comando",
             " ",
             " ",
             " ",
             " "
         ],
         "diff": [
-            "",
+            "differenza",
             " ",
             " ",
             " ",
             " "
         ],
         "discussion": [
-            "",
-            " ",
+            "discussioni",
+            "discussione",
             " ",
             " ",
             " "
         ],
         "help": [
-            "",
+            "aiuto",
             " ",
             " ",
             " ",
             " "
         ],
         "info": [
-            "",
+            "informazioni",
             " ",
             " ",
             " ",
             " "
         ],
         "invite": [
-            "",
+            "invita",
             " ",
             " ",
             " ",
             " "
         ],
         "overview": [
-            "",
+            "panoramica",
             " ",
             " ",
             " ",
             " "
         ],
         "page": [
-            "",
+            "pagina",
             " ",
             " ",
             " ",
             " "
         ],
         "random": [
-            "",
+            "casuale",
             " ",
             " ",
             " ",
             " "
         ],
         "search": [
-            "",
+            "ricerca",
             " ",
             " ",
             " ",
             " "
         ],
         "test": [
-            "",
+            "prova",
             " ",
             " ",
             " ",
             " "
         ],
         "user": [
-            "",
+            "utente",
             " ",
             " ",
             " ",
             " "
         ],
         "verify": [
-            "",
+            "verifica",
             " ",
             " ",
             " ",
@@ -113,14 +113,15 @@
     },
     "dateformat": "it-IT",
     "diff": {
-        "badrev": "almeno una revisione non esiste!",
+        "badrev": "Non esiste nemmeno una revisione!",
         "hidden": "*nascosta*",
         "info": {
             "added": "Aggiunto:",
-            "bytes": "$1 Byte",
+            "bytes": "$1 byte",
             "comment": "Commento:",
-            "editor": "Editore:",
-            "more": "Ed altro ancora",
+            "editor": "Utente:",
+            "minor": "_(m)",
+            "more": "E altro ancora",
             "removed": "Rimosso:",
             "size": "Differenza:",
             "tags": "Tag:",
@@ -144,15 +145,16 @@
         " "
     ],
     "general": {
-        "default": "questo server non è ancora impostato. Usa $1 o la dashboard per cambiare le impostazioni.",
-        "disclaimer": "Sono un piccolo bot con lo scopo di facilitare i collegamenti e la ricerca in siti MediaWiki come i wiki Gamepedia e Fandom. Mostro delle piccole descrizioni ed informazioni aggiuntive riguardo le pagine ed inoltre sono capace di risolvere rinvii e seguire collegamenti interwiki. $1 mi ha scritto in JavaScript.\n\nPuoi supportarmi su Patreon:",
+        "database": "⚠️ **Funzionalità limitata** ⚠️\nNessuna impostazione trovata, per favore contatta il proprietario del bot!",
+        "default": "Questo server non è ancora impostato. Usa $1 o la dashboard per cambiare le impostazioni.",
+        "disclaimer": "Sono un piccolo bot con lo scopo di facilitare i collegamenti e le ricerche sui siti MediaWiki come Wikipedia e le wiki di Fandom. Mostro brevi descrizioni e informazioni aggiuntive sulle pagine e sono capace di risolvere i redirect e seguire i collegamenti interwiki. $1 mi ha scritto in JavaScript.\n\nPuoi supportarmi su Patreon:",
         "experimental": "**Questa funzione è sperimentale! Non è garantito il suo corretto funzionamento e potrebbe essere rimossa in futuro.**",
         "helpserver": "Per domande e problemi visita il mio server di supporto:",
         "limit": "🚨 **Stop, hai raggiunto il limite!** 🚨\n\n$1, il tuo messaggio contiene troppi comandi!",
         "missingperm": "Mi mancano alcuni permessi per questo comando:",
-        "patreon": "questa è una funzione esclusiva per i Patreon!\nPuoi supportarmi su Patreon per avere accesso a questa funzione:",
-        "prefix": "il prefisso per questo server è `$1`. Puoi cambiare il prefisso con `$1settings prefix`. Per un elenco di tutti i comandi vedi `$1help`.",
-        "readonly": "**il database è attualmente nella modalità di sola lettura, non puoi cambiare alcuna impostazione per adesso!**"
+        "patreon": "Questa è una funzione esclusiva per i Patreon!\nPuoi supportarmi su Patreon per avere accesso a questa funzione:",
+        "prefix": "Il prefisso per questo server è `$1`. Puoi cambiare il prefisso con `$1settings prefix`. Per un elenco di tutti i comandi vedi `$1help`.",
+        "readonly": "**Il database è attualmente nella modalità di sola lettura, non puoi cambiare alcuna impostazione per adesso!**"
     },
     "help": {
         "admin": "Questi comandi possono essere eseguiti solo dagli amministratori:",
@@ -162,35 +164,35 @@
         "list": {
             "default": {
                 "cmd": "<termine di ricerca>",
-                "desc": "Ti risponderò con un collegamento all'articolo corrispondente nel wiki."
+                "desc": "Ti risponderò con un collegamento all'articolo corrispondente sulla wiki."
             },
             "diff": {
                 "id": {
                     "cmd": "diff <diff> [<oldid>]",
-                    "desc": "Ti risponderò con un collegamento alla diff nel wiki."
+                    "desc": "Ti risponderò con un collegamento alla diff sulla wiki."
                 },
                 "name": {
                     "cmd": "diff <nome pagina>",
-                    "desc": "Ti risponderò con un collegamento all'ultima diff dell'articolo nel wiki."
+                    "desc": "Ti risponderò con un collegamento all'ultima diff dell'articolo sulla wiki."
                 }
             },
             "discussion": {
                 "post": {
                     "cmd": "discussion post <termine di ricerca>",
-                    "desc": "Ti risponderò con un collegamento al post della discussione corrispondente nel wiki Fandom."
+                    "desc": "Ti risponderò con un collegamento al post della discussione corrispondente sulla wiki di Fandom."
                 },
                 "thread": {
                     "cmd": "discussion <termine di ricerca>",
-                    "desc": "Ti risponderò con un collegamento al thread della discussione corrispondente nel wiki Fandom."
+                    "desc": "Ti risponderò con un collegamento al thread della discussione corrispondente sulla wiki di Fandom."
                 }
             },
             "fandom": {
                 "cmd": "?<wiki> <termine di ricerca>",
-                "desc": "Ti risponderò con un collegamento all'articolo corrispondente nel wiki Fandom nominato: `https://<wiki>.fandom.com/`"
+                "desc": "Ti risponderò con un collegamento all'articolo corrispondente sulla wiki di Fandom richiesta: `https://<wiki>.fandom.com/`"
             },
             "gamepedia": {
                 "cmd": "!<wiki> <termine di ricerca>",
-                "desc": "Ti risponderò con un collegamento all'articolo corrispondente nel wiki Gamepedia nominato: `https://<wiki>.gamepedia.com/`"
+                "desc": "Ti risponderò con un collegamento all'articolo corrispondente sulla wiki di Gamepedia richiesta: `https://<wiki>.gamepedia.com/`"
             },
             "help": {
                 "admin": {
@@ -213,11 +215,11 @@
             "inline": {
                 "link": {
                     "cmd": "[[<nome pagina>]]",
-                    "desc": "Ti risponderò con un collegamento diretto all'articolo nel wiki."
+                    "desc": "Ti risponderò con un collegamento diretto all'articolo sulla wiki."
                 },
                 "template": {
                     "cmd": "{{<nome pagina>}}",
-                    "desc": "Ti risponderò con un collegamento all'articolo nel wiki."
+                    "desc": "Ti risponderò con un collegamento all'articolo sulla wiki."
                 }
             },
             "minecraft": {
@@ -227,24 +229,24 @@
                 },
                 "command": {
                     "cmd": "command <comando di Minecraft>",
-                    "desc": "Ti risponderò con la sintassi del comando di Minecraft ed un collegamento all'articolo del comando in Minecraft Wiki."
+                    "desc": "Ti risponderò con la sintassi del comando di Minecraft ed un collegamento all'articolo del comando sulla Minecraft Wiki."
                 },
                 "default": {
                     "cmd": "/<comando di Minecraft>",
-                    "desc": "Ti risponderò con la sintassi del comando di Minecraft ed un collegamento all'articolo del comando in Minecraft Wiki."
+                    "desc": "Ti risponderò con la sintassi del comando di Minecraft ed un collegamento all'articolo del comando sulla Minecraft Wiki."
                 }
             },
             "mwprojects": {
                 "cmd": "!!<wiki> <termine di ricerca>",
-                "desc": "Ti risponderò con un collegamento all'articolo corrispondente nel progetto MediaWiki nominato. Esempio: `$1!!it.wikipedia.org Cookie`"
+                "desc": "Ti risponderò con un collegamento all'articolo corrispondente nel progetto MediaWiki richiesto. Esempio: `$1!!it.wikipedia.org Cookie`"
             },
             "overview": {
                 "cmd": "overview",
-                "desc": "Ti mostrerò qualche informazione e statistica riguardo il wiki."
+                "desc": "Ti mostrerò qualche informazione e statistica sulla wiki."
             },
             "page": {
                 "cmd": "page <nome pagina>",
-                "desc": "Ti risponderò con un collegamento diretto all'articolo nel wiki."
+                "desc": "Ti risponderò con un collegamento diretto all'articolo sulla wiki."
             },
             "pause": {
                 "active": {
@@ -253,12 +255,12 @@
                 },
                 "inactive": {
                     "cmd": "pause $1",
-                    "desc": "Ignorerò tutti i comandi su questo server, ad eccezione di alcuni comandi amministrativi."
+                    "desc": "Ignorerò tutti i comandi su questo server, ad eccezione di alcuni comandi per amministratori."
                 }
             },
             "random": {
                 "cmd": "random",
-                "desc": "Ti risponderò con un collegamento ad una pagian casuale sul wiki."
+                "desc": "Ti risponderò con un collegamento ad una pagina casuale sulla wiki."
             },
             "rcscript": {
                 "add": {
@@ -278,20 +280,21 @@
                     "desc": "Cambierò la modalità di visualizzazione del webhook delle ultime modifiche."
                 },
                 "feeds": {
-                    "cmd": "rcscript feeds"
+                    "cmd": "rcscript feeds",
+                    "desc": "Aggiungerò/rimuoverò l'attività nelle discussioni di una wiki di Fandom nel webhook delle ultime modifiche."
                 },
                 "lang": {
                     "cmd": "rcscript lang <nuova lingua>",
                     "desc": "Cambierò la lingua del webhook delle ultime modifiche."
                 },
                 "wiki": {
-                    "cmd": "rcscript wiki <nuovo wiki>",
-                    "desc": "Cambierò il wiki del webhook delle ultime modifiche."
+                    "cmd": "rcscript wiki <nuova wiki>",
+                    "desc": "Cambierò la wiki del webhook delle ultime modifiche."
                 }
             },
             "search": {
                 "cmd": "search <termine di ricerca>",
-                "desc": "Ti risponderò con un collegamento diretto alla paigna di ricerca per l'articolo nel wiki."
+                "desc": "Ti risponderò con un collegamento diretto alla pagina di ricerca per l'articolo sulla wiki."
             },
             "settings": {
                 "channel": {
@@ -303,7 +306,8 @@
                     "desc": "Cambierò le impostazioni per questo server."
                 },
                 "inline": {
-                    "cmd": "settings inline toggle"
+                    "cmd": "settings inline toggle",
+                    "desc": "Attiverò/disattiverò i comandi in linea per questo server."
                 },
                 "lang": {
                     "cmd": "settings lang <lingua>",
@@ -313,14 +317,18 @@
                     "cmd": "settings prefix <prefisso>",
                     "desc": "Cambierò il prefisso per questo server."
                 },
+                "role": {
+                    "cmd": "settings role <ruolo>",
+                    "desc": "Cambierò il ruolo minimo richiesto per usare i comandi su questo server."
+                },
                 "wiki": {
                     "cmd": "settings wiki <wiki>",
-                    "desc": "Cambierò il wiki predefinito per questo server."
+                    "desc": "Cambierò la wiki predefinita per questo server."
                 }
             },
             "test": {
                 "cmd": "test",
-                "desc": "Se sono attivo rispondo! Altrimenti no."
+                "desc": "Se sono attivo, risponderò! Altrimenti no."
             },
             "user": {
                 "cmd": "User:<nome utente>",
@@ -329,39 +337,334 @@
             "verification": {
                 "accountage": {
                     "cmd": "verification <id> accountage <nuova età account>",
-                    "desc": "Cambierò l'età dell'account minima per la verifica nel wiki (in giorni)."
+                    "desc": "Cambierò l'età minima dell'account (in giorni) per la verifica tramite wiki."
                 },
                 "add": {
                     "cmd": "verification add <ruolo>",
-                    "desc": "Aggiungerò una nuova verifica del wiki. Accetta una lista separata da `|`."
+                    "desc": "Aggiungerò una nuova verifica tramite wiki. Accetta una lista separata da `|`."
                 },
                 "channel": {
                     "cmd": "verification <id> channel <nuovo canale>",
-                    "desc": "Cambierò il canale per la verifica del wiki. Accetta una lista separata da `|`."
+                    "desc": "Cambierò il canale per la verifica tramite wiki. Accetta una lista separata da `|`."
                 },
                 "default": {
                     "cmd": "verification",
-                    "desc": "Cambierò le verifiche del wiki usate dal comando `$1verify`."
+                    "desc": "Cambierò le verifiche tramite wiki usate dal comando `$1verify`."
                 },
                 "delete": {
                     "cmd": "verification <id> delete",
-                    "desc": "Cancellerò la verifica del wiki."
+                    "desc": "Cancellerò la verifica tramite wiki."
                 },
                 "editcount": {
                     "cmd": "verification <id> editcount <nuovo numero modifiche>",
-                    "desc": "Cambierò il numero di modifiche minime per la verifica del wiki."
+                    "desc": "Cambierò il numero di modifiche minime per la verifica tramite wiki."
+                },
+                "postcount": {
+                    "cmd": "verification <id> postcount <nuovo numero di post>",
+                    "desc": "Cambierò il numero minimo di post in discussioni per la verifica tramite wiki.\n\t• Inserisci un numero negativo per richiedere o il numero di modifiche o quello di post.\n\t• Inserisci `null` per controllare il numero combinato di modifiche e post."
                 },
                 "rename": {
-                    "cmd": "verification <id> rename"
+                    "cmd": "verification <id> rename",
+                    "desc": "Cambierò se il soprannome dell'utente su Discord debba essere modificato nel nome utente sulla wiki o meno durante la verifica tramite wiki."
+                },
+                "role": {
+                    "cmd": "verification <id> role <nuovo ruolo>",
+                    "desc": "Cambierò il ruolo per la verifica tramite wiki. Accetta una lista separata da `|`."
+                },
+                "usergroup": {
+                    "cmd": "verification <id> usergroup <nuovo gruppo utente>",
+                    "desc": "Cambierò il gruppo utente per la verifica tramite wiki. Accetta una lista separata da `|`.\n\t• Inserisci `AND` come primo elemento della lista per richiedere tutti i gruppi utente forniti."
                 }
+            },
+            "verify": {
+                "cmd": "verify <nome utente wiki>",
+                "desc": "Usa questo comando per verificare il tuo account Discord tramite il tuo account sulla wiki e ottenere i ruoli corrispondenti al tuo account sulla wiki."
+            },
+            "voice": {
+                "cmd": "voice",
+                "desc": "Cerco di dare a tutti in un canale vocale un ruolo specifico."
+            },
+            "wikia": {
+                "cmd": "??<wiki> <termine di ricerca>",
+                "desc": "Ti risponderò con un collegamento all'articolo corrispondente sulla wiki di Wikia richiesta: `https://<wiki>.wikia.org/`"
             }
-        }
+        },
+        "noadmin": "Hai bisogno del permesso `Gestire Server`per questi comandi!",
+        "pause": "**Sono attualmente in pausa su questo server!**\nPosso eseguire solo questi comandi:"
+    },
+    "interaction": {
+        "dashboard": {
+            "perm_allow": "Acconsenti",
+            "perm_default": "Default",
+            "perm_deny": "Nega",
+            "updated": "$1 ha aggiornato le sovrascritture per i permessi del comando slash `$2`."
+        },
+        "inline": "Per favore fornisci del testo con link [[wikitext]] per usare questo comando.",
+        "nowiki": "La wiki utilizzata non esiste!",
+        "verify": "Per favore fornisci il tuo nome utente sulla wiki per usare questo comando per verificare il tuo account Discord tramite il tuo account sulla wiki e ottenere i ruoli corrispondenti al tuo account sulla wiki."
+    },
+    "invite": {
+        "bot": "Usa questo link per invitarmi su un altro server:"
+    },
+    "minecraft": {
+        "fixed": "{{PLURAL:$1|Versione corretta|Versioni corrette}}:",
+        "issue_link": {
+            "Blocks": {
+                "inward": "è bloccato da $1",
+                "outward": "blocca $1"
+            },
+            "Bonfire Testing": {
+                "inward": "scoperto testando $1",
+                "outward": "il test ha scoperto $1"
+            },
+            "Cloners": {
+                "inward": "è clonato da $1",
+                "outward": "clona $1"
+            },
+            "Duplicate": {
+                "inward": "è duplicato in $1",
+                "outward": "è duplicato di $1"
+            },
+            "Relates": {
+                "inward": "è correlato a $1",
+                "outward": "è correlato a $1"
+            }
+        },
+        "more": "E $1 in più.",
+        "private": "**Problema privato**",
+        "status": {
+            "Awaiting Response": "In attesa di risposta",
+            "Cannot Reproduce": "Impossibile riprodurre",
+            "Closed": "Chiuso",
+            "Done": "Fatto",
+            "Duplicate": "Duplicato",
+            "Fixed": "Corretto",
+            "In Progress": "In corso",
+            "Incomplete": "Incompleto",
+            "Invalid": "Invalido",
+            "Open": "Aperto",
+            "Postponed": "Posposto",
+            "Reopened": "Riaperto",
+            "Resolved": "Risolto",
+            "Won't Fix": "Non sarà corretto",
+            "Works As Intended": "Funziona come atteso"
+        },
+        "total": "$1 {{PLURAL:$2|problema corretto|problemi corretti}}"
+    },
+    "overview": {
+        "admins": "Amministratori:",
+        "articles": "Articoli:",
+        "comments": "Commenti agli articoli:",
+        "created": "Creata:",
+        "crossover": "Fusa con:",
+        "description": "Descrizione:",
+        "edits": "Modifiche:",
+        "founder": "Fondatore:",
+        "image": "Immagine:",
+        "inaccurate": "Le statistiche potrebbero essere inesatte",
+        "lang": "Lingua:",
+        "license": "Licenza:",
+        "manager": "Rappresentante della wiki:",
+        "misermode": "Modalità miser:",
+        "no": "No",
+        "none": "*nessuna*",
+        "official": "Wiki ufficiale:",
+        "pages": "Pagine totali:",
+        "posts": "Post in Discussioni:",
+        "readonly": "Questa wiki è attualmente in modalità sola lettura!",
+        "rtl": "Da destra a sinistra:",
+        "talk": "discussione",
+        "topic": "Argomento:",
+        "unknown": "*sconosciuto*",
+        "users": "Utenti attivi:",
+        "version": "Versione:",
+        "vertical": "Categoria:",
+        "walls": "Post in bacheca:",
+        "wikiid": "Wiki ID:",
+        "yes": "Sì"
+    },
+    "pause": {
+        "off": "Non sono più in pausa su questo server e risponderò di nuovo a tutti i comandi!",
+        "on": "Sono ora in pausa su questo server e ignorerò la maggior parte dei comandi!"
+    },
+    "rcscript": {
+        "ad": "Vuoi le ultime modifiche direttamente su Discord? Usa `$1rcscript` per aggiungere un webhook delle ultime modifiche basato su **$2** al tuo server Discord!",
+        "add_more": "Aggiungi altri webhook delle ultime modifiche:",
+        "added": "Un webhook per le ultime modifiche è stato aggiunto per:",
+        "all_inactive": "Non puoi disabilitare allo stesso tempo le modifiche sulla wiki e le modifiche sui feed.",
+        "audit_reason": "Webhook delle ultime modifiche per \"$1\"",
+        "audit_reason_delete": "Rimosso webhook delle ultime modifiche",
+        "audit_reason_edit": "Aggiornato webhook delle ultime modifiche",
+        "avatar": "Avatar del webhook:",
+        "blocked": "Questa wiki è stata bloccata dall'essere aggiunta come webhook delle ultime modifiche!",
+        "blocked_reason": "Questa wiki è stata bloccata dall'essere aggiunta come webhook delle ultime modifiche per `$1`!",
+        "channel": "Canale:",
+        "current": "Questi sono i webhook delle ultime modifiche attuali per questo server:",
+        "current_display": "La modalità di visualizzazione per questo webhook è:",
+        "current_lang": "La lingua per questo webhook è:",
+        "current_selected": "Questo è il webhook delle ultime modifiche `$1` per questo server:",
+        "current_wiki": "La wiki per questo webhook è:",
+        "dashboard": {
+            "added": "$1 ha aggiunto il webhook delle ultime modifiche con id `$2`.",
+            "removed": "$1 ha rimosso il webhook delle ultime modifiche con id `$2`.",
+            "updated": "$1 ha aggiornato il webhook delle ultime modifiche con id `$2`."
+        },
+        "delete": "Elimina questo webhook delle ultime modifiche:",
+        "deleted": "Il webhook delle ultime modifiche è stato eliminato.",
+        "disabled": "disattivato",
+        "disabled_feeds": "L'attività basata sui feed, come Discussioni, bacheche e commenti agli articoli, è stata disattivata per questo webhook.",
+        "disabled_rc": "Le modifiche alla wiki sono state disattivate per questo webhook.",
+        "display": "Modalità di visualizzazione:",
+        "enabled": "attivato",
+        "enabled_feeds": "L'attività basata sui feed, come Discussioni, bacheche e commenti agli articoli, è stata attivata per questo webhook.",
+        "enabled_rc": "Le modifiche alla wiki sono state attivate per questo webhook.",
+        "feeds": "Attività nei feed:",
+        "help_display_compact": "Messaggi di testo compatti con link in linea.",
+        "help_display_diff": "Messaggi incorporati con anteprima delle immagini e differenze di modifica.",
+        "help_display_embed": "Messaggi incorporati con tag di modifica e cambi di categoria.",
+        "help_display_image": "Messaggi incorporati con anteprima delle immagini.",
+        "help_feeds": "(discussioni, bacheche, commenti agli articoli)",
+        "help_lang": "Le lingue attualmente supportate sono:",
+        "help_wiki": "Linka ad un sito MediaWiki come `https://<wiki>.fandom.com/`",
+        "lang": "Lingua:",
+        "max_entries": "Hai già raggiunto il numero massimo di webhook delle ultime modifiche.",
+        "missing": "Non ci sono ancora webhook delle ultime modifiche su questo server.",
+        "name": "Nome del webhook:",
+        "new_lang": "<nuova lingua>",
+        "new_wiki": "<link alla wiki>",
+        "no_feeds": "La wiki di questo webhook non ha nessuna funzione feed, come discussioni, bacheche o commenti agli articoli, abilitata.",
+        "noadmin": "Hai bisogno del permesso `Gestire i webhook` per questo comando!",
+        "rc": "Modifiche sulla wiki:",
+        "sysmessage": "Il messaggio di sistema `$1` deve contenere il server id `$2` per aggiungere un webhook delle ultime modifiche.",
+        "title": "Webhook delle ultime modifiche",
+        "toggle": "(attiva/disattiva)",
+        "updated_display": "La modalità di visualizzazione di questo webhook è stata cambiata in:",
+        "updated_lang": "La lingua per questo webhook è stata cambiata in:",
+        "updated_wiki": "La wiki per questo webhook è stata cambiata in:",
+        "webhook": {
+            "blocked": "Questo webhook delle ultime modifiche sarà eliminato perché la wiki è stata bloccata!",
+            "blocked_help": "Puoi chiedere maggiori dettagli sul [server di supporto]($1).",
+            "blocked_reason": "Questo webhook delle ultime modifiche sarà eliminato perché la wiki è stata bloccata per `$1`!",
+            "created": "Un webhook delle ultime modifiche per $1 è stato aggiunto a questo canale.",
+            "dashboard": {
+                "avatar": "• L'avatar del webhook è stata cambiato.",
+                "channel": "• Il webhook è stato spostato in questo canale.",
+                "disabled_feeds": "• L'attività dei feed, come discussioni, bacheche e commenti agli articoli, è stata rimossa.",
+                "disabled_rc": "• Le modifiche sulla wiki sono state rimosse.",
+                "display_compact": "• La modalità di visualizzazione è stata cambiata in messaggi di testo compatti con link in linea.",
+                "display_diff": "• La modalità di visualizzazione è stata cambiata in messaggi incorporati con anteprima delle immagini e differenze di modifica.",
+                "display_embed": "• La modalità di visualizzazione è stata cambiata in messaggi incorporati con tag di modifica e cambi di categoria.",
+                "display_image": "• La modalità di visualizzazione è stata cambiata in messaggi incorporati con anteprima delle immagini.",
+                "enabled_feeds": "• L'attività dei feed, come discussioni, bacheche e commenti agli articoli, è stata aggiunta.",
+                "enabled_rc": "• Le modifiche sulla wiki sono state aggiunte.",
+                "lang": "• La lingua è stata cambiata in $1.",
+                "name": "• Il nome del webhook è stato cambiato in \"$1\".",
+                "updated": "Questo webhook delle ultime modifiche è stato aggiornato:",
+                "wiki": "• La wiki è stata cambiata in $1."
+            },
+            "deleted": "Questo webhook delle ultime modifiche sarà eliminato.",
+            "disabled_feeds": "Le attività dei feed, come discussioni, bacheche e commenti agli articoli, è stata rimossa da questo webhook delle ultime modifiche.",
+            "disabled_rc": "Le modifiche sulla wiki sono state rimosse da questo webhook delle ultime modifiche.",
+            "enabled_feeds": "L'attività dei feed, come discussioni, bacheche e commenti agli articoli, è stata aggiunta a questo webhook delle ultime modifiche.",
+            "enabled_rc": "Le modifiche sulla wiki sono state aggiunte a questo webhook delle ultime modifiche.",
+            "updated_display_compact": "La modalità di visualizzazione è stata cambiata in messaggi di testo compatti con link in linea per questo webhook delle ultime modifiche.",
+            "updated_display_diff": "La modalità di visualizzazione è stata cambiata in messaggi incorporati con anteprima delle immagini e differenze di modifica per questo webhook delle ultime modifiche.",
+            "updated_display_embed": "La modalità di visualizzazione è stata cambiata in messaggi incorporati con tag di modifica e cambi di categoria per questo webhook delle ultime modifiche.",
+            "updated_display_image": "La modalità di visualizzazione è stata cambiata in messaggi incorporati con anteprima delle immagini per questo webhook delle ultime modifiche.",
+            "updated_lang": "La lingua è stata cambiata in `$1` per questo webhook delle ultime modifiche.",
+            "updated_wiki": "La wiki è stata cambiata in $1 per questo webhook delle ultime modifiche."
+        },
+        "webhook_failed": "Sfortunatamente non è stato possibile creare il webhook, per favore riprova più tardi.",
+        "wiki": "Wiki:"
+    },
+    "search": {
+        "cached": "Ultimo aggiornamento cache:",
+        "category": {
+            "content": "Contenuto di questa categoria:",
+            "empty": "*Questa categoria è vuota*",
+            "files": "$1 file",
+            "pages": "$1 {{PLURAL:$2|pagina|pagine}}",
+            "subcats": "$1 {{PLURAL:$2|categoria|categorie}}"
+        },
+        "empty": "*Questa pagina speciale è vuota*",
+        "infopage": "Non è il risultato corretto? Usa $1 per un link diretto.",
+        "infosearch": "Non è il risultato corretto? Usa $1 per un link diretto oppure $2 per una lista di tutte le corrispondenze.",
+        "loading": "Caricamento descrizione della pagina in corso…",
+        "media": "Alla pagina di descrizione del file",
+        "messagedefault": "Default:",
+        "messagedefaultnone": "*nessuno*",
+        "results": "$1 {{PLURAL:$2|risultato totale|risultati totali}}",
+        "special": "Contenuto di questa pagina speciale:"
+    },
+    "settings": {
+        "button": "Usa la Dashboard",
+        "channel current": "Queste sono le impostazioni attuali per questo canale:",
+        "channel lang": "La lingua per questo canale è:",
+        "channel langchanged": "Hai cambiato la lingua per questo canale in:",
+        "channel role": "Il ruolo minimo richiesto per questo canale è:",
+        "channel rolechanged": "Hai cambiato il ruolo minimo richiesto per questo canale in:",
+        "channel wiki": "La wiki di default per questo canale è:",
+        "channel wikichanged": "Hai cambiato la wiki di default per questo canale in:",
+        "current": "Queste sono le impostazioni attuali per questo server:",
+        "currentchannel": "Sovrascritture del canale:",
+        "currentinline": "Comandi in linea:",
+        "currentlang": "Lingua:",
+        "currentprefix": "Prefisso:",
+        "currentrole": "Ruolo minimo:",
+        "currentwiki": "Wiki di default:",
+        "dashboard": {
+            "channel": "$1 ha aggiornato le impostazioni per $2.",
+            "removed": "$1 ha rimosso le impostazioni per $2.",
+            "updated": "$1 ha aggiornato le impostazioni del server."
+        },
+        "foundwikis": "Intendi una di queste wiki?",
+        "inline disabled": {
+            "channel inline": "I comandi in linea sono attualmente disabilitati per questo canale.",
+            "channel inlinechanged": "Hai disabilitato i comandi in linea per questo canale.",
+            "help": "Usa `$1` per abilitare i comandi in linea come `[[$2]]` e `{{$2}}`.",
+            "inline": "I comandi in linea sono attualmente disabilitati su questo server.",
+            "inlinechanged": "Hai disabilitato i comandi in linea su questo server."
+        },
+        "inline enabled": {
+            "channel inline": "I comandi in linea sono attualmente abilitati per questo canale.",
+            "channel inlinechanged": "Hai abilitato i comandi in linea per questo canale.",
+            "help": "Usa `$1` per disabilitare i comandi in linea come `[[$2]]` e `{{$2}}`.",
+            "inline": "I comandi in linea sono attualmente abilitati su questo server.",
+            "inlinechanged": "Hai abilitato i comandi in linea su questo server."
+        },
+        "lang": "La lingua per questo server è:",
+        "langchanged": "Hai cambiato la lingua per questo server in:",
+        "langhelp": "Usa `$1 <language>` per cambiare la lingua.\nLe lingue attualmente supportate sono:",
+        "langinvalid": "La lingua specificata non è supportata!",
+        "missing": "Questo server non è ancora impostato. Usa $1 e $2 per cambiare le impostazioni.",
+        "nochannels": "*Ancora niente sovrascritture di canali*",
+        "prefix": "Il prefisso per questo server è:",
+        "prefixchanged": "Hai cambiato il prefisso per questo server in:",
+        "prefixhelp": "Usa `$1 <prefix>` per cambiare il prefisso.\nUsa `_` alla fine per indicare uno spazio alla fine del prefisso.\nIl prefisso non può includere menzioni!",
+        "prefixinvalid": "Il prefisso specificato non è supportato!",
+        "role": "Il ruolo minimo richiesto per questo server è:",
+        "rolechanged": "Hai cambiato il ruolo minimo richiesto per questo server in:",
+        "rolehelp": "Usa `$1 <role>` per cambiare il ruolo minimo richiesto.",
+        "roleinvalid": "Il ruolo specificato non esiste!",
+        "save_failed": "Sfortunatamente non è stato possibile salvare le impostazioni, per favore riprova più tardi.",
+        "wiki": "La wiki di default per questo server è:",
+        "wikichanged": "Hai cambiato la wiki di default per questo server in:",
+        "wikihelp": "Usa `$1 <link>` per cambiare la wiki di default.\nLinka ad un sito MediaWiki come `https://<wiki>.fandom.com/`",
+        "wikiinvalid": "Per favore fornisci un link valido ad un sito MediaWiki, come Wikipedia o una wiki di Fandom!",
+        "wikiinvalid_http": "Il sito web fornito non ha un certificato TLS/SSL valido! Per motivi di sicurezza sono supportate solo le wiki che usano HTTPS.\nSe sei l'amministratore del sito, puoi ottenere un certificato da un'autorità di certificazione come *Let’s Encrypt*:\n<https://letsencrypt.org/it/getting-started/>",
+        "wikiinvalid_private": "La wiki fornita è privata! Solo le wiki pubbliche che possono essere lette da tutti sono supportate.",
+        "wikiinvalid_timeout": "Il link fornito ha impiegato troppo tempo a rispondere!",
+        "wikimissing": "Non è ancora stata impostata una wiki di default per questo server!"
     },
     "test": {
+        "MediaWiki": "Richiede almeno $1 per le funzionalità complete, trovato `$2`.",
+        "notice": "Funzionalità limitate",
+        "pause": "Sono attualmente in pausa su questo server.",
+        "slow": "Scusa, sembro essere un po' lento in questo momento.",
         "text": [
-            "",
-            " ",
+            "Sono pienamente operativo!",
             " ",
+            "E credimi, sono ancora vivo.",
             " ",
             " ",
             " ",
@@ -389,25 +692,161 @@
             " ",
             " ",
             " "
-        ]
+        ],
+        "time": "Tempo di risposta"
+    },
+    "user": {
+        "block": {
+            "duration": {
+                "days": "$1 {{PLURAL:$2|giorno|giorni}}",
+                "hours": "$1 {{PLURAL:$2|ora|ore}}",
+                "minutes": "$1 {{PLURAL:$2|minuto|minuti}}",
+                "months": "$1 {{PLURAL:$2|mese|mesi}}",
+                "separator": ",_",
+                "separator_last": "_e_",
+                "weeks": "$1 {{PLURAL:$2|settimana|settimane}}",
+                "years": "$1 {{PLURAL:$2|anno|anni}}"
+            },
+            "header": "$1 è attualmente bloccato!",
+            "indef_noreason": "Bloccato indefinitamente il $1 da $4.",
+            "indef_text": "Bloccato indefinitamente il $1 da $4 con motivazione \"$5\".",
+            "noreason": "Bloccato il $1 per $2 fino al $3 da $4.",
+            "text": "Bloccato il $1 per $2 fino al $3 da $4 con motivazione \"$5\"."
+        },
+        "gblock": {
+            "disabled": "Questo account è attualmente disabilitato!",
+            "header": "$1 è attualmente bloccato globalmente!"
+        },
+        "gender": {
+            "female": "Femmina",
+            "male": "Maschio",
+            "unknown": "Sconosciuto"
+        },
+        "info": {
+            "discord": "Discord:",
+            "editcount": "Conteggio delle modifiche:",
+            "favwiki": "Wiki preferita:",
+            "gender": "Sesso:",
+            "globaleditcount": "Conteggio globale delle modifiche:",
+            "globalgroup": "{{PLURAL:$1|Gruppo globale|Gruppi globali}}:",
+            "group": "{{PLURAL:$1|Gruppo|Gruppi}}:",
+            "loading": "Caricamento statistiche globali in corso…",
+            "postcount": "Post in Discussioni:",
+            "registration": "Data di registrazione:",
+            "wikisedited": "Wiki modificate:"
+        }
+    },
+    "verification": {
+        "accountage": "Età dell'account:",
+        "add_more": "Aggiungi altre verifiche:",
+        "added": "La verifica è stata aggiunta:",
+        "and": "e",
+        "channel": "Canale:",
+        "channel_max": "Hai fornito troppi canali.",
+        "channel_missing": "Il canale fornito non esiste.",
+        "current": "Queste sono le verifiche attuali su questo server:",
+        "current_selected": "Questa è la verifica `$1` di questo server:",
+        "dashboard": {
+            "added": "$1 ha aggiunto la verifica con id `$2`.",
+            "added_notice": "$1 ha aggiunto alcuni avvisi di verifica.",
+            "removed": "$1 ha rimosso la verifica con id `$2`.",
+            "updated": "$1 ha aggiornato la verifica con id `$2`.",
+            "updated_notice": "$1 ha aggiornato alcuni avvisi di verifica."
+        },
+        "delete_current": "Elimina questa verifica:",
+        "deleted": "La verifica è stata eliminata.",
+        "disabled": "disabilitata",
+        "editcount": "Conteggio delle modifiche:",
+        "enabled": "abilitata",
+        "flag_logall": "Log delle verifiche senza successo:",
+        "flag_private": "Invio delle risposte al comando di verifica privatamente:",
+        "indays": "(in giorni)",
+        "logging": "Canale del log:",
+        "match": "Avviso di requisito mancante:",
+        "max_entries": "Hai già raggiunto il numero massimo di verifiche.",
+        "missing": "Non ci sono ancora verifiche su questo server.",
+        "new_accountage": "<nuova età account>",
+        "new_channel": "<nuovo canale>",
+        "new_editcount": "<nuovo conteggio modifiche>",
+        "new_postcount": "<nuovo conteggio post>",
+        "new_role": "<nuovo ruolo>",
+        "new_usergroup": "<nuovo gruppo utente>",
+        "no_role": "Per favore fornisci un ruolo per la nuova verifica.",
+        "notice_embed": "Alcuni avvisi includono link scritti in markdown. Assicurati che il bot abbia il permesso `Incorporare i link` in tutti i canali di verifica affinché funzionino correttamente.",
+        "or": "o",
+        "postcount": "Conteggio dei post (solo per le wiki di Fandom):",
+        "postcount_or": "(alternativa al conteggio delle modifiche)",
+        "posteditcount": "Conteggio delle modifiche e dei post combinati:",
+        "rename": "Modifica il soprannome:",
+        "rename_no_permission": "**$1 non ha il permesso `Gestire i soprannomi`per forzare i nomi utente della wiki!**",
+        "role_add": "Ruolo da aggiungere:",
+        "role_deleted": "**Il ruolo $1 non sembra esistere più!**",
+        "role_managed": "Il ruolo fornito non può essere assegnato.",
+        "role_max": "Hai fornito troppi ruoli.",
+        "role_missing": "Il ruolo fornito non esiste.",
+        "role_none": "nessuno",
+        "role_remove": "Ruoli da rimuovere:",
+        "role_too_high": "**Il ruolo $1 è troppo alto per essere assegnato da $2!**",
+        "save_failed": "Sfortunatamente non è stato possibile salvare la verifica, per favore riprova più tardi.",
+        "success": "Avviso di successo:",
+        "toggle": "(attiva/disattiva)",
+        "updated": "La verifica è stata aggiornata:",
+        "usergroup": "Gruppo utente:",
+        "usergroup_max": "Hai fornito troppi gruppi utente.",
+        "usergroup_too_long": "Il gruppo utente fornito è troppo lungo.",
+        "value_too_high": "Il valore fornito è troppo alto."
     },
     "verify": {
-        "user_gblocked_reply": "il tuo utente wiki **\"$1\" collegato è bloccato globalmente!**",
+        "audit_reason": "Verificato come \"$1\"",
+        "button_again": "Controlla di nuovo",
+        "button_wrong_user": "Non puoi controllare di nuovo questa verifica, questa è una verifica di $1!",
+        "discord": "Utente Discord:",
+        "empty": "*vuoto*",
+        "error": "La verifica è fallita a causa di un errore.",
+        "error_reply": "La verifica è fallita a causa di un errore, per favore prova ancora.",
+        "failed_gblock": "**Controllo per blocchi globali fallito!**",
+        "failed_rename": "**Modifica del suo soprannome Discord fallita!**",
+        "failed_roles": "**Aggiunta dei ruoli fallita**",
+        "footer": "Verifica dell'account wiki",
+        "help_fandom": "https://community.fandom.com/wiki/Special:VerifyUser",
+        "help_gamepedia": "https://help.fandom.com/wiki/Gamepedia_Help_Wiki:Discord_verification",
+        "help_guide": "Segui [questa guida]($1) per aggiungere il tuo tag Discord al tuo profilo della wiki:",
+        "help_missing": "Per favore assicurati di usare il tuo nome utente sulla wiki e che le maiuscole/minuscole siano corrette.",
+        "help_subpage": "Per favore aggiungi il tuo tag Discord ($1) nella tua sottopagina Discord sulla wiki:",
+        "missing": "Non ci sono verifiche impostate per questo canale.",
+        "notice": "Avviso:",
+        "oauth_button": "Autenticati",
+        "oauth_message": "Per favore usa [questo link]($1) per autenticare il tuo account sulla wiki.",
+        "oauth_message_dm": "Per favore usa questo link per autenticare il tuo account sulla wiki per $1.",
+        "oauth_private": "La wiki utilizza OAuth2 per la verifica. Per favore attiva i messaggi diretti da questo server o usa il comando `/verify` così che possa inviarti il link di autenticazione privatamente.",
+        "oauth_used": "*Verificato usando OAuth2*",
+        "qualified_add": "Aggiunto a:",
+        "qualified_add_error": "Non può essere aggiunto a:",
+        "qualified_remove": "Rimosso da:",
+        "qualified_remove_error": "Non può essere rimosso da:",
+        "user_blocked": "**L'utente della wiki $1 è bloccato!**",
+        "user_blocked_reply": "il tuo utente della wiki collegato **\"$1\" è bloccato!**",
+        "user_disabled": "**L'account della wiki $1 è disabilitato!**",
+        "user_disabled_reply": "il tuo account della wiki collegato **\"$1\" è disabilitato!**",
+        "user_failed": "L'utente Discord $1 non corrisponde all'utente $2 della wiki.",
+        "user_failed_reply": "il tuo tag Discord non corrisponde all'utente della wiki \"$1\".",
+        "user_gblocked": "**L'utente della wiki $1 è bloccato globalmente!**",
+        "user_gblocked_reply": "il tuo utente della wiki collegato **\"$1\" è bloccato globalmente!**",
         "user_matches": "L'utente Discord $1 corrisponde all'utente wiki $2, ma non soddisfa i requisiti per alcun ruolo.",
-        "user_matches_reply": "il tuo tag Discord corrisponde all'utente wiki \"$1\", ma non soddisfi i requisiti per alcun ruolo.",
-        "user_missing": "L'utente wiki \"$1\" non esiste.",
-        "user_missing_reply": "il tuo utente wiki \"$1\" collegato non esiste.",
-        "user_renamed": "Il suo soprannome Discord è stato cambiato nel suo nome utente wiki.",
-        "user_verified": "L'utente Discord $1 è {{GENDER:$3|stato|stata|statə}} {{GENDER:$3|verificato|verificata|verificatə}} con successo come utente wiki $2.",
-        "user_verified_reply": "sei {{GENDER:$2|stato|stata|statə}} {{$GENDER:$2|verificato|verificata|verificatə}} con successo come utente wiki \"$1\".",
+        "user_matches_reply": "il tuo tag Discord corrisponde all'utente della wiki \"$1\", ma non soddisfi i requisiti per alcun ruolo.",
+        "user_missing": "L'utente della wiki \"$1\" non esiste.",
+        "user_missing_reply": "il tuo utente della wiki collegato \"$1\" non esiste.",
+        "user_renamed": "Il suo soprannome Discord è stato cambiato nel suo nome utente sulla wiki.",
+        "user_verified": "L'utente Discord $1 è {{GENDER:$3|stato verificato|stata verificata|statə verificatə}} con successo come l'utente della wiki $2.",
+        "user_verified_reply": "sei {{GENDER:$2|stato verificato|stata verificata|statə verificatə}} con successo come l'utente della wiki \"$1\".",
         "wiki": "Utente wiki:"
     },
     "voice": {
         "channel": "Canale vocale",
         "disable": "Usa `$1` per disabilitare questa funzione.",
-        "disabled": "hai disabilitato la funzione per aggiungere ruoli per i canali vocali.",
+        "disabled": "Hai disabilitato la funzione per aggiungere ruoli per i canali vocali.",
         "enable": "Usa `$1` per abilitare questa funzione.",
-        "enabled": "hai abilitato questa funzione per aggiungere ruoli ai canali vocali.",
+        "enabled": "Hai abilitato questa funzione per aggiungere ruoli ai canali vocali.",
         "join": "$1 è entrato nel canale vocale \"$2\".",
         "left": "$1 ha lasciato il canale vocale \"$2\".",
         "name": "nome canale vocale",

+ 29 - 3
i18n/ja.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "ja-JP",
     "diff": {
-        "badrev": "2つ以上の差分が存在しません!",
+        "badrev": "1つ以上の差分が存在しません!",
         "hidden": "*隠された*",
         "info": {
             "added": "追加:",
@@ -478,13 +478,39 @@
         "unknown": "*不明*",
         "users": "アクティブユーザー:",
         "version": "バージョン:",
-        "vertical": "縦型:"
+        "vertical": "縦型:",
+        "walls": "ディスカッションウォールへの書き込み:",
+        "wikiid": "Wiki ID:",
+        "yes": "はい"
+    },
+    "pause": {
+        "off": "私はこのサーバーで動作を続けており、すべてのコマンドに反応します!",
+        "on": "私はこのサーバーで動作を停止しており、ほとんどのコマンドに反応しません!"
+    },
+    "rcscript": {
+        "ad": "Discordで直接最近の変更を表示したいですか? `$1rcscript`を使って、あなたのDiscordサーバーに **$2** に基づいた最近の変更点のWebHookを追加してください!",
+        "add_more": "より最近の変更のWebHookを追加:",
+        "added": "最近の変更に関するwebhookが追加されました:",
+        "all_inactive": "Wikiの編集とフィードの変更の両方のトラッキングを同時にオフにすることはできません。",
+        "audit_reason": "\"$1\"に対する最近の編集のWebhook",
+        "audit_reason_delete": "最近の編集のWebhookを削除",
+        "audit_reason_edit": "更新された最近の変更のWebhook",
+        "avatar": "Webhook アバター:",
+        "blocked": "このWikiは、最近の変更のWebhookとして追加されるのがブロックされています!",
+        "blocked_reason": "このWikiは、以下の理由でWebhookへの新しい編集の追加がブロックされています: `$1`!",
+        "channel": "チャンネル:",
+        "current": "これらは、このサーバーの現在の最新の変更点を示すウェブフックです。",
+        "current_display": "このWebhookの表示モード:",
+        "current_lang": "このWebhookの言語:",
+        "current_selected": "このサーバーに現在存在する最近の更新 `$1` の Webhook。",
+        "current_wiki": "このWebhookのWiki:"
     },
     "test": {
+        "slow": "申し訳ありませんが、私は今、少し遅いようです。",
         "text": [
             "",
             " ",
-            " ",
+            " そして、信じてください、私はまだ生きています。",
             " ",
             " ",
             " ",

+ 1 - 0
i18n/nl.json

@@ -120,6 +120,7 @@
             "bytes": "$1 {{PLURAL:$2|Byte|Bytes}}",
             "comment": "Beschrijving:",
             "editor": "Bewerker:",
+            "minor": "_(m)",
             "more": "En meer",
             "removed": "Verwijderd:",
             "size": "Verschil:",

+ 85 - 84
i18n/pl.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "pl-PL",
     "diff": {
-        "badrev": "przynajmniej jedna zmiana nie istnieje!",
+        "badrev": "Przynajmniej jedna zmiana nie istnieje!",
         "hidden": "*ukryte*",
         "info": {
             "added": "Dodano:",
@@ -146,15 +146,15 @@
     ],
     "general": {
         "database": "⚠️ **Niepełna funkcjonalność** ⚠️\nNie znaleziono ustawień, skontaktuj się z właścicielem bota!",
-        "default": "ten serwer nie został jeszcze skonfigurowany. Użyj $1 lub panelu sterowania aby zmienić ustawienia.",
+        "default": "Ten serwer nie został jeszcze skonfigurowany. Użyj $1 lub panelu sterowania, aby zmienić ustawienia.",
         "disclaimer": "Jestem małym botem, którego zadaniem jest linkowanie do stron i wyszukiwanie na różnych wiki działających na MediaWiki, takich jak Wikipedia czy Fandom. Wyświetlam krótkie opisy i dodatkowe informacje o stronach, a także jestem w stanie śledzić przekierowania. Zostałem napisany w języku JavaScript przez $1.\n\nMożesz wesprzeć mnie na Patreonie:",
         "experimental": "**Ta funkcja jest w fazie eksperymentalnej! Może nie działać poprawnie bądź zostać usunięta w przyszłości.**",
         "helpserver": "W przypadku pytań lub problemów odwiedź mój serwer:",
         "limit": "🚨 **Chwila moment! Osiągnięto limit!** 🚨\n\n$1, Twoja wiadomość zawiera zbyt wiele komend!",
         "missingperm": "Brakuje mi uprawnień:",
-        "patreon": "to funkcja tylko dla patronów!\nMożesz wesprzeć moją pracę na Patreonie, aby uzyskać dostęp do tej funkcji:",
-        "prefix": "prefiksem komend dla tego serwera jest `$1`. Możesz zmienić prefiks, używając `$1settings prefix`. Lista wszystkich komend jest dostępna przez użycie `$1pomoc`.",
-        "readonly": "**baza danych jest obecnie w trybie tylko do odczytu, zmiana ustawień obecnie nie jest możliwa!**"
+        "patreon": "To funkcja tylko dla patronów!\nMożesz wesprzeć moją pracę na Patreonie, aby uzyskać dostęp do tej funkcji:",
+        "prefix": "Prefiksem komend dla tego serwera jest `$1`. Możesz zmienić prefiks, używając `$1settings prefix`. Lista wszystkich komend jest dostępna przez użycie `$1pomoc`.",
+        "readonly": "**Baza danych jest obecnie w trybie tylko do odczytu, zmiana ustawień obecnie nie jest możliwa!**"
     },
     "help": {
         "admin": "Te komendy mogą być wykonane jedynie przez administrację:",
@@ -389,7 +389,7 @@
                 "desc": "Odpowiem z pasującym linkiem do podanej wiki na Wikia: `https://<wiki>.wikia.org/`"
             }
         },
-        "noadmin": "potrzebujesz uprawnienia `Zarządzanie serwerem` aby używać tych komend!",
+        "noadmin": "Potrzebujesz uprawnienia `Zarządzanie serwerem` aby używać tych komend!",
         "pause": "**Moja praca na tym serwerze została wstrzymana!**\nTylko następujące komendy mogą być używane:"
     },
     "interaction": {
@@ -490,34 +490,34 @@
     "rcscript": {
         "ad": "Chcesz mieć ostatnie zmiany na Discordzie? Użyj `$1rcscript` aby dodać webhook ostatnich zmian bazowany na **$2** do swojego serwera Discord!",
         "add_more": "Dodaj więcej webhooków ostatnich zmian:",
-        "added": "dodano webhook ostatnich zmian dla:",
-        "all_inactive": "śledzenie ostatnich zmian oraz dyskusji nie może być wyłączone jednocześnie.",
+        "added": "Dodano webhook ostatnich zmian dla:",
+        "all_inactive": "Śledzenie ostatnich zmian oraz dyskusji nie może być wyłączone jednocześnie.",
         "audit_reason": "Webhook ostatnich zmian dla „$1”",
         "audit_reason_delete": "Usunięto webhook ostatnich zmian",
         "audit_reason_edit": "Zaktualizowano webhook ostatnich zmian",
         "avatar": "Awatar webhooka:",
-        "blocked": "ta wiki nie może zostać dodana do webhooka ostatnich zmian gdyż została zablokowana!",
-        "blocked_reason": "zablokowano możliwość dodawania tej wiki do webhooka ostatnich zmian z powodu `$1`!",
+        "blocked": "Ta wiki nie może zostać dodana do webhooka ostatnich zmian, gdyż została zablokowana!",
+        "blocked_reason": "Zablokowano możliwość dodawania tej wiki do webhooka ostatnich zmian z powodu `$1`!",
         "channel": "Kanał:",
-        "current": "to są istniejące webhooki ostatnich zmian dla tego serwera:",
-        "current_display": "tryb wyświetlania webhooka jest ustawiony na:",
-        "current_lang": "językiem tego webhooka jest:",
-        "current_selected": "to jest webhook ostatnich zmian `$1` dla tego serwera:",
-        "current_wiki": "wiki dla tego webhooka to:",
+        "current": "To są istniejące webhooki ostatnich zmian dla tego serwera:",
+        "current_display": "Tryb wyświetlania webhooka jest ustawiony na:",
+        "current_lang": "Językiem tego webhooka jest:",
+        "current_selected": "To jest webhook ostatnich zmian `$1` dla tego serwera:",
+        "current_wiki": "Wiki dla tego webhooka to:",
         "dashboard": {
             "added": "$1 dodał webhook ostatnich zmian z identyfikatorem `$2`.",
             "removed": "$1 usunął webhook ostatnich zmian z identyfikatorem `$2`.",
             "updated": "$1 zaktualizował webhook ostatnich zmian z identyfikatorem `$2`."
         },
         "delete": "Usuń ten webhook ostatnich zmian:",
-        "deleted": "webhook ostatnich zmian został usunięty.",
+        "deleted": "Webhook ostatnich zmian został usunięty.",
         "disabled": "wyłączone",
-        "disabled_feeds": "postowanie zmian z Dyskusji dla tego webhooka zostało wyłączone.",
-        "disabled_rc": "śledzenie ostatnich zmian na wiki zostało wyłączone.",
+        "disabled_feeds": "Postowanie zmian z Dyskusji dla tego webhooka zostało wyłączone.",
+        "disabled_rc": "Śledzenie ostatnich zmian na wiki zostało wyłączone.",
         "display": "Tryb wyświetlania:",
         "enabled": "włączone",
-        "enabled_feeds": "postowanie zmian z Dyskusji dla tego webhooka zostało włączone.",
-        "enabled_rc": "śledzenie ostatnich zmian na wiki zostało włączone.",
+        "enabled_feeds": "Postowanie zmian z Dyskusji dla tego webhooka zostało włączone.",
+        "enabled_rc": "Śledzenie ostatnich zmian na wiki zostało włączone.",
         "feeds": "Zmiany bazowane na Feeds:",
         "help_display_compact": "Kompaktowe wiadomości.",
         "help_display_diff": "Szczegółowe wiadomości z podglądami zdjęć oraz podglądem zmian edycji.",
@@ -527,20 +527,20 @@
         "help_lang": "Aktualnie wspieranymi językami są:",
         "help_wiki": "Użyj linku do wiki na bazie MediaWiki takiej jak `https://<wiki>.fandom.com`",
         "lang": "Język:",
-        "max_entries": "osiągnięto maksymalną ilość webhooków ostatnich zmian.",
-        "missing": "obecnie nie istnieją żadne webhooki ostatnich zmian na tym serwerze.",
+        "max_entries": "Osiągnięto maksymalną ilość webhooków ostatnich zmian.",
+        "missing": "Obecnie nie istnieją żadne webhooki ostatnich zmian na tym serwerze.",
         "name": "Nazwa webhooka:",
         "new_lang": "<nowy język>",
         "new_wiki": "<link do wiki>",
-        "no_feeds": "wiki dla tego webhooka nie ma włączonej usługi Dyskusji.",
-        "noadmin": "potrzebujesz uprawnienia `Zarządzaj webhookami` aby używać tej komendy!",
+        "no_feeds": "Wiki dla tego webhooka nie ma włączonej usługi Dyskusji.",
+        "noadmin": "Potrzebujesz uprawnienia `Zarządzaj webhookami` aby używać tej komendy!",
         "rc": "Ostatnie zmiany:",
-        "sysmessage": "wiadomość systemowa `$1` musi zawierać numer ID tego serwera `$2` aby dodać webhook ostatnich zmian.",
+        "sysmessage": "Wiadomość systemowa `$1` musi zawierać numer ID tego serwera `$2` aby dodać webhook ostatnich zmian.",
         "title": "Webhook ostatnich zmian",
         "toggle": "(przełącznik)",
-        "updated_display": "tryb wyświetlania dla tego webhooka został zmieniony na:",
-        "updated_lang": "język tego webhooka został zmieniony na:",
-        "updated_wiki": "wiki dla tego webhooka została zmieniona na:",
+        "updated_display": "Tryb wyświetlania dla tego webhooka został zmieniony na:",
+        "updated_lang": "Język tego webhooka został zmieniony na:",
+        "updated_wiki": "Wiki dla tego webhooka została zmieniona na:",
         "webhook": {
             "blocked": "Webhook ostatnich zmian dla tej wiki zostanie usunięty gdyż wiki została zablokowana!",
             "blocked_help": "Możesz zapytać się o szczegóły na [serwerze wsparcia]($1).",
@@ -574,10 +574,11 @@
             "updated_lang": "Język został zmieniony na `$1` dla tego webhooka ostatnich zmian.",
             "updated_wiki": "Ta wiki została zmieniona na $1 dla tego webhooka ostatnich zmian."
         },
-        "webhook_failed": "niestety, webhook nie mógł zostać utworzony, spróbuj ponownie później.",
+        "webhook_failed": "Niestety, webhook nie mógł zostać utworzony, spróbuj ponownie później.",
         "wiki": "Wiki:"
     },
     "search": {
+        "cached": "Ostatnia aktualizacja pamięci podręcznej:",
         "category": {
             "content": "Zawartość tej kategorii:",
             "empty": "*Ta kategoria jest pusta*",
@@ -595,14 +596,14 @@
     },
     "settings": {
         "button": "Otwórz panel sterowania",
-        "channel current": "obecne ustawienia dla tego kanału:",
-        "channel lang": "językiem tego kanału jest:",
-        "channel langchanged": "zmieniono język kanału na:",
+        "channel current": "Obecne ustawienia dla tego kanału:",
+        "channel lang": "Językiem tego kanału jest:",
+        "channel langchanged": "Zmieniono język kanału na:",
         "channel role": "Minimalną rolą dla tego kanału jest:",
-        "channel rolechanged": "zmieniono rolę wymaganą dla tego kanału do:",
-        "channel wiki": "domyślną wiki tego kanału jest:",
-        "channel wikichanged": "zmieniono domyślną wiki dla tego kanału na:",
-        "current": "obecne ustawienia dla tego serwera:",
+        "channel rolechanged": "Zmieniono rolę wymaganą dla tego kanału do:",
+        "channel wiki": "Domyślną wiki tego kanału jest:",
+        "channel wikichanged": "Zmieniono domyślną wiki dla tego kanału na:",
+        "current": "Obecne ustawienia dla tego serwera:",
         "currentchannel": "Ustawienia specjalne kanału:",
         "currentinline": "Linkowanie składnią wiki:",
         "currentlang": "Język:",
@@ -616,42 +617,42 @@
         },
         "foundwikis": "Czy chodziło Ci o jedną z tych wiki?",
         "inline disabled": {
-            "channel inline": "linkowanie składnią wiki jest obecnie wyłączone dla tego kanału.",
-            "channel inlinechanged": "wyłączono linkowanie składnią wiki dla tego kanału.",
+            "channel inline": "Linkowanie składnią wiki jest obecnie wyłączone dla tego kanału.",
+            "channel inlinechanged": "Wyłączono linkowanie składnią wiki dla tego kanału.",
             "help": "Użyj `$1` aby włączyć linkowanie składnią wiki takiego jak `[[$2]]` czy `{{$2}}`.",
-            "inline": "linkowanie składnią wiki jest obecnie wyłączone dla tego serwera.",
-            "inlinechanged": "wyłączono linkowanie składnią wiki dla tego serwera."
+            "inline": "Linkowanie składnią wiki jest obecnie wyłączone dla tego serwera.",
+            "inlinechanged": "Wyłączono linkowanie składnią wiki dla tego serwera."
         },
         "inline enabled": {
-            "channel inline": "linkowanie składnią wiki jest obecnie włączone dla tego kanału.",
-            "channel inlinechanged": "włączono linkowanie składnią wiki dla tego kanału.",
+            "channel inline": "Linkowanie składnią wiki jest obecnie włączone dla tego kanału.",
+            "channel inlinechanged": "Włączono linkowanie składnią wiki dla tego kanału.",
             "help": "Użyj `$1` aby wyłączyć linkowanie składnią wiki takiego jak `[[$2]]` czy `{{$2}}`.",
-            "inline": "linkowanie składnią wiki jest obecnie włączone dla tego serwera.",
-            "inlinechanged": "włączono linkowanie składnią wiki dla tego serwera."
+            "inline": "Linkowanie składnią wiki jest obecnie włączone dla tego serwera.",
+            "inlinechanged": "Włączono linkowanie składnią wiki dla tego serwera."
         },
-        "lang": "językiem tego serwera jest:",
-        "langchanged": "zmieniono język serwera na:",
+        "lang": "Językiem tego serwera jest:",
+        "langchanged": "Zmieniono język serwera na:",
         "langhelp": "Użyj `$1 <język>` aby zmienić język.\nObecnie wspieranymi językami są:",
-        "langinvalid": "podany język nie jest wspierany!",
-        "missing": "ten serwer nie został jeszcze skonfigurowany. Użyj $1 oraz $2 aby zmienić ustawienia.",
+        "langinvalid": "Podany język nie jest wspierany!",
+        "missing": "Ten serwer nie został jeszcze skonfigurowany. Użyj $1 oraz $2, aby zmienić ustawienia.",
         "nochannels": "*Brak ustawień specjalnych kanału*",
-        "prefix": "prefiks dla tego serwera:",
-        "prefixchanged": "zmieniono prefiks dla tego serwera na:",
+        "prefix": "Prefiks dla tego serwera:",
+        "prefixchanged": "Zmieniono prefiks dla tego serwera na:",
         "prefixhelp": "Użyj `$1 <prefiks>` aby zmienić prefiks.\nUżyj `_` na końcu jako zamiennik spacji na końcu prefiksu.\nPrefiks nie może zawierać wzmianek!",
-        "prefixinvalid": "podany prefiks nie jest wspierany!",
-        "role": "minimalną rolą wymaganą dla tego serwera jest:",
-        "rolechanged": "zmieniono rolę wymaganą dla tego serwera na:",
+        "prefixinvalid": "Podany prefiks nie jest wspierany!",
+        "role": "Minimalną rolą wymaganą dla tego serwera jest:",
+        "rolechanged": "Zmieniono rolę wymaganą dla tego serwera na:",
         "rolehelp": "Użyj `$1 <rola>` aby zmienić wymaganą rolę.",
-        "roleinvalid": "podana rola nie istnieje!",
-        "save_failed": "niestety, ustawienia nie mogły zostać zapisane, spróbuj ponownie później.",
-        "wiki": "domyślną wiki tego serwera jest:",
-        "wikichanged": "zmieniono domyślną wiki dla tego serwera na:",
+        "roleinvalid": "Podana rola nie istnieje!",
+        "save_failed": "Niestety, ustawienia nie mogły zostać zapisane, spróbuj ponownie później.",
+        "wiki": "Domyślną wiki tego serwera jest:",
+        "wikichanged": "Zmieniono domyślną wiki dla tego serwera na:",
         "wikihelp": "Użyj `$1 <link>` aby zmienić domyślną wiki.\nLink do wiki na bazie MediaWiki w stylu `https://<wiki>.fandom.com/`",
-        "wikiinvalid": "podaj poprawny link do strony wiki, np. na Wikipedii lub Fandomie!",
+        "wikiinvalid": "Podaj poprawny link do strony wiki, np. na Wikipedii lub Fandomie!",
         "wikiinvalid_http": "Strona internetowa pod podanym linkiem nie posiada poprawnego certyfikatu TLS/SSL! Z powodów bezpieczeństwa tylko strony używające HTTPS są wspierane.\nJeżeli jesteś administratorem strony, możesz uzyskać certyfikat od centrum certyfikacji takiego jak na przykład *Let’s Encrypt*:\n<https://letsencrypt.org/getting-started/>",
-        "wikiinvalid_private": "podana wiki jest prywatna! Tylko publiczne wiki, które mogą być czytane przez wszystkich są wspierane.",
-        "wikiinvalid_timeout": "wczytywanie podanego linku zajęło zbyt długo!",
-        "wikimissing": "domyślna wiki nie została ustawiona dla tego serwera!"
+        "wikiinvalid_private": "Podana wiki jest prywatna! Wspierane są tylko publiczne wiki, które mogą być czytane przez wszystkich.",
+        "wikiinvalid_timeout": "Wczytywanie podanego linku zajęło zbyt długo!",
+        "wikimissing": "Domyślna wiki nie została ustawiona dla tego serwera!"
     },
     "test": {
         "MediaWiki": "Wymaga przynajmniej $1 dla pełnej funkcjonalności, znaleziono `$2`.",
@@ -735,13 +736,13 @@
     "verification": {
         "accountage": "Wiek konta:",
         "add_more": "Dodaj więcej weryfikacji:",
-        "added": "weryfikacja została dodana:",
+        "added": "Weryfikacja została dodana:",
         "and": "oraz",
         "channel": "Kanał:",
-        "channel_max": "podano zbyt dużo kanałów.",
-        "channel_missing": "podany kanał nie istnieje.",
-        "current": "lista obecnych weryfikacji na tym serwerze:",
-        "current_selected": "to weryfikacja `$1` dla tego serwera:",
+        "channel_max": "Podano zbyt dużo kanałów.",
+        "channel_missing": "Podany kanał nie istnieje.",
+        "current": "Lista obecnych weryfikacji na tym serwerze:",
+        "current_selected": "To weryfikacja `$1` dla tego serwera:",
         "dashboard": {
             "added": "$1 dodał weryfikację z id `$2`.",
             "added_notice": "$1 dodał(a) dodatkowe powiadomienia weryfikacji.",
@@ -750,7 +751,7 @@
             "updated_notice": "$1 zaktualizował(a) dodatkowe powiadomienia weryfikacji."
         },
         "delete_current": "Usuń tę weryfikację:",
-        "deleted": "weryfikacja została usunięta.",
+        "deleted": "Weryfikacja została usunięta.",
         "disabled": "wyłączone",
         "editcount": "Ilość edycji:",
         "enabled": "włączone",
@@ -759,15 +760,15 @@
         "indays": "(w dniach)",
         "logging": "Kanał logujący:",
         "match": "Powiadomienie o niespełnionych wymaganiach:",
-        "max_entries": "osiągnięto maksymalną ilość weryfikacji.",
-        "missing": "na tym serwerze nie istnieją żadne weryfikacje na tę chwilę.",
+        "max_entries": "Osiągnięto maksymalną ilość weryfikacji.",
+        "missing": "Na tym serwerze nie istnieją żadne weryfikacje na tę chwilę.",
         "new_accountage": "<nowy wiek konta>",
         "new_channel": "<nowy kanał>",
         "new_editcount": "<nowa ilość edycji>",
         "new_postcount": "<nowa ilość postów>",
         "new_role": "<nowa rola>",
         "new_usergroup": "<nowa grupa użytkowników>",
-        "no_role": "podaj rolę dla nowej weryfikacji.",
+        "no_role": "Podaj rolę dla nowej weryfikacji.",
         "or": "lub",
         "postcount": "Ilość postów (tylko na wiki Fandomu):",
         "postcount_or": "(alternatywa do zliczania edycji)",
@@ -776,20 +777,20 @@
         "rename_no_permission": "**$1 wymaga uprawnienia `Zarządzanie pseudonimami` do zmiany nazw użytkowników na wiki!**",
         "role_add": "Role do dodania:",
         "role_deleted": "**Nie wygląda na to aby rola $1 nadal istniała!**",
-        "role_managed": "podana rola nie może zostać ustawiona.",
-        "role_max": "podano zbyt dużo ról.",
-        "role_missing": "podana rola nie istnieje.",
+        "role_managed": "Podana rola nie może zostać ustawiona.",
+        "role_max": "Podano zbyt dużo ról.",
+        "role_missing": "Podana rola nie istnieje.",
         "role_none": "brak",
         "role_remove": "Role do usunięcia:",
         "role_too_high": "**Rola $1 jest wyżej niż najwyższa rola $2, dlatego nie będzie możliwe jej ustawienie!**",
-        "save_failed": "niestety, weryfikacja nie mogła zostać zapisana, spróbuj ponownie później.",
+        "save_failed": "Niestety, weryfikacja nie mogła zostać zapisana, spróbuj ponownie później.",
         "success": "Powiadomienie o sukcesie:",
         "toggle": "(przełącz)",
-        "updated": "weryfikacja została zaktualizowana:",
+        "updated": "Weryfikacja została zaktualizowana:",
         "usergroup": "Grupa użytkownika:",
-        "usergroup_max": "podano zbyt dużo grup użytkowników.",
-        "usergroup_too_long": "podana grupa użytkowników jest zbyt długa.",
-        "value_too_high": "podana wartość jest zbyt wysoka."
+        "usergroup_max": "Podano zbyt dużo grup użytkowników.",
+        "usergroup_too_long": "Podana grupa użytkowników jest zbyt długa.",
+        "value_too_high": "Podana wartość jest zbyt wysoka."
     },
     "verify": {
         "audit_reason": "Zweryfikowano jako „$1”",
@@ -798,7 +799,7 @@
         "discord": "{{GENDER:$1|Użytkownik Discord|Użytkowniczka Discord|Użytkownik/Użytkowniczka Discord}}:",
         "empty": "*puste*",
         "error": "Weryfikacja nie powiodła się z powodu błędu.",
-        "error_reply": "weryfikacja nie powiodła się z powodu błędu, spróbuj ponownie.",
+        "error_reply": "Weryfikacja nie powiodła się z powodu błędu, spróbuj ponownie.",
         "failed_gblock": "**Sprawdzenie globalnych blokad nie powiodło się!**",
         "failed_rename": "**Zmiana nazwy użytkownika nie powiodła się!**",
         "failed_roles": "**Dodawanie ról nie powiodło się!**",
@@ -808,12 +809,12 @@
         "help_guide": "Podążaj za [tym poradnikiem]($1) w celu dodania nazwy użytkownika Discord na profil wiki:",
         "help_missing": "Upewnij się że używasz swojego nicku z wiki oraz wielkość liter w nicku się zgadza.",
         "help_subpage": "Dodaj swoją nazwę użytkownika Discord ($1) do podstrony Discord na wiki:",
-        "missing": "nie ustawiono weryfikacji dla tego kanału.",
+        "missing": "Nie ustawiono weryfikacji dla tego kanału.",
         "notice": "Powiadomienie:",
         "oauth_button": "Uwierzytelnij się",
-        "oauth_message": "użyj [tego linku]($1) aby uwierzytelnić swoje konto wiki.",
+        "oauth_message": "Użyj [tego linku]($1), aby uwierzytelnić swoje konto wiki.",
         "oauth_message_dm": "Użyj tego linku aby uwierzytelnić swoje konto wiki dla $1.",
-        "oauth_private": "wiki używa OAuth2 do weryfikacji. Aby się uwierzytelnić, zezwól na komunikację przez prywatne wiadomości dla tego serwera lub użyj komendy `/verify` aby zweryfikować się za pomocą linku wysłanego prywatnie.",
+        "oauth_private": "Wiki używa OAuth2 do weryfikacji. Aby się uwierzytelnić, zezwól na komunikację przez prywatne wiadomości dla tego serwera lub użyj komendy `/verify` aby zweryfikować się za pomocą linku wysłanego prywatnie.",
         "oauth_used": "*Zweryfikowano z użyciem OAuth2*",
         "qualified_add": "Dodano do:",
         "qualified_add_error": "Nie udało się dodać:",
@@ -839,9 +840,9 @@
     "voice": {
         "channel": "Kanał głosowy",
         "disable": "Użyj `$1` aby wyłączyć tę funkcję.",
-        "disabled": "wyłączono funkcję nadawania ról dla osób korzystających z czatów głosowych.",
+        "disabled": "Wyłączono funkcję nadawania ról dla osób korzystających z czatów głosowych.",
         "enable": "Użyj `$1` aby włączyć tę funkcję.",
-        "enabled": "włączono funkcję nadawania ról dla osób korzystających z czatów głosowych.",
+        "enabled": "Włączono funkcję nadawania ról dla osób korzystających z czatów głosowych.",
         "join": "$1 dołączył do „$2”.",
         "left": "$1 wyszedł z „$2”.",
         "name": "nazwa kanału głosowego",

+ 85 - 83
i18n/pt-br.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "pt-PT",
     "diff": {
-        "badrev": "pelo menos uma revisão não existe!",
+        "badrev": "Pelo menos uma revisão não existe!",
         "hidden": "*oculto*",
         "info": {
             "added": "Adicionado:",
@@ -146,15 +146,15 @@
     ],
     "general": {
         "database": "⚠️ **Funcionalidade Limitada** ⚠️\nNenhuma configuração encontrada, entre em contato com o proprietário do robô!",
-        "default": "este servidor ainda não está configurado. Use $1 ou o painel para alterar as configurações.",
+        "default": "Este servidor ainda não está configurado. Use $1 ou o painel para alterar as configurações.",
         "disclaimer": "Eu sou um robô com a tarefa de vincular sites com Mediawiki como Wikipéedia e da Fandom. Mostro descrições curtas e informações adicionais sobre as páginas e sou capaz de resolver redirecionamentos e seguir os links de interwiki. $1 me escreveu em JavaScript.\n\nVocê pode me apoiar no Patreon:",
         "experimental": "**Este recurso é experimental! Não é garantido que funcione corretamente e pode ser removido no futuro.**",
         "helpserver": "Para dúvidas e problemas, visite o meu servidor de suporte:",
         "limit": "🚨 **Pare, isso é demais para mim!** 🚨\n\n$1, sua mensagem continha muitos comandos!",
         "missingperm": "Eu não tenho permissão para executar este comando:",
-        "patreon": "esse é um recurso exclusivo do Patreon!\nVocê pode me apoiar no Patreon para obter acesso a esse recurso:",
-        "prefix": "o prefixo para este servidor é `$1`. Você pode alterar o prefixo com `$1settings prefix`. Para uma lista de todos os comandos, veja `$1help`.",
-        "readonly": "**a base de dados está temporariamente no modo somente leitura, você não pode alterar as configurações agora!**"
+        "patreon": "Esse é um recurso exclusivo do Patreon!\nVocê pode me apoiar no Patreon para obter acesso a esse recurso:",
+        "prefix": "O prefixo para este servidor é `$1`. Você pode alterar o prefixo com `$1settings prefix`. Para uma lista de todos os comandos, veja `$1help`.",
+        "readonly": "**A base de dados está temporariamente no modo somente leitura, você não pode alterar as configurações agora!**"
     },
     "help": {
         "admin": "Esses comandos só podem ser executados por administradores:",
@@ -389,7 +389,7 @@
                 "desc": "Vou responder com um link para um artigo correspondente na wiki da Wikia: `https://<wiki>.wikia.org/`"
             }
         },
-        "noadmin": "você precisa da permissão de `Gerenciar servidor` para esses comandos!",
+        "noadmin": "Você precisa da permissão de `Gerenciar servidor` para esses comandos!",
         "pause": "**Estou atualmente em pausa neste servidor!**\nSom estes comandos podem ser executados:"
     },
     "interaction": {
@@ -490,34 +490,34 @@
     "rcscript": {
         "ad": "Deseja alterações recentes diretamente no Discord? Usar `$1rcscript` para adicionar um webhook de alterações recentes com base em **$2** para o seu servidor no Discord!",
         "add_more": "Adicione webhooks de mudanças recentes:",
-        "added": "um webhook de mudanças recentes foi adicionado para:",
-        "all_inactive": "você não pode ter mudanças da wiki e alterações baseadas em feeds desativadas ao mesmo tempo.",
+        "added": "Um webhook de mudanças recentes foi adicionado para:",
+        "all_inactive": "Você não pode ter mudanças da wiki e alterações baseadas em feeds desativadas ao mesmo tempo.",
         "audit_reason": "Mudanças recentes no webhook para \"$1\"",
         "audit_reason_delete": "Removido o webhook das mudanças recentes",
         "audit_reason_edit": "Webhook de mudanças recentes atualizado",
         "avatar": "Avatar do webhook:",
-        "blocked": "essa wiki foi impedido de ser adicionado como um webhook de mudanças recentes!",
-        "blocked_reason": "essa wiki foi bloqueada de ser adicionado como um webhook de mudanças recentes para `$1`!",
+        "blocked": "Essa wiki foi impedido de ser adicionado como um webhook de mudanças recentes!",
+        "blocked_reason": "Essa wiki foi bloqueada de ser adicionado como um webhook de mudanças recentes para `$1`!",
         "channel": "Canal:",
-        "current": "estas são as webhooks para as mudanças recentes para este servidor:",
-        "current_display": "o modo de exibição para este webhook é:",
-        "current_lang": "o idioma para este webhook é:",
-        "current_selected": "estas são as mudanças recentes no webhook `$1` para este servidor:",
-        "current_wiki": "a wiki para este webhook é:",
+        "current": "Estas são as webhooks para as mudanças recentes para este servidor:",
+        "current_display": "O modo de exibição para este webhook é:",
+        "current_lang": "O idioma para este webhook é:",
+        "current_selected": "Estas são as mudanças recentes no webhook `$1` para este servidor:",
+        "current_wiki": "A wiki para este webhook é:",
         "dashboard": {
             "added": "$1 adicionou o webhook das mudanças recentes com id `$2`.",
             "removed": "$1 removeu o webhook das mudanças recentes com id `$2`.",
             "updated": "$1 atualizou o webhook das mudanças recentes com id `$2`."
         },
         "delete": "Exclua as mudanças recentes no webhook:",
-        "deleted": "o webhook das mudanças recentes foi excluído.",
+        "deleted": "O webhook das mudanças recentes foi excluído.",
         "disabled": "desativado",
-        "disabled_feeds": "as mudanças baseadas em feeds, como discussões, murais de mensagens e comentários de artigos, para este webhook foram desativadas.",
-        "disabled_rc": "as mudanças da wiki para esse webhook foram desativadas.",
+        "disabled_feeds": "As mudanças baseadas em feeds, como discussões, murais de mensagens e comentários de artigos, para este webhook foram desativadas.",
+        "disabled_rc": "As mudanças da wiki para esse webhook foram desativadas.",
         "display": "Modo de exibição:",
         "enabled": "ativado",
-        "enabled_feeds": "as mudanças baseadas em feeds, como discussões, murais de mensagens e comentários de artigos, para este webhook foram ativadas.",
-        "enabled_rc": "as mudanças wiki para esse webhook foram ativadas.",
+        "enabled_feeds": "As mudanças baseadas em feeds, como discussões, murais de mensagens e comentários de artigos, para este webhook foram ativadas.",
+        "enabled_rc": "As mudanças da wiki para esse webhook foram ativadas.",
         "feeds": "Mudanças baseadas em feeds:",
         "help_display_compact": "Compactar mensagens de texto com links embutidos.",
         "help_display_diff": "Incorpore mensagens com visualizações de imagens e diferenças de edição.",
@@ -527,20 +527,20 @@
         "help_lang": "Os idiomas suportados atualmente são:",
         "help_wiki": "Link para um site MediaWiki como:`https://<wiki>.fandom.com/`",
         "lang": "Idioma:",
-        "max_entries": "você já atingiu a quantidade máxima de webhooks de mudanças recentes.",
-        "missing": "ainda não há webhooks de mudanças recentes para este servidor.",
+        "max_entries": "Você já atingiu a quantidade máxima de webhooks de mudanças recentes.",
+        "missing": "Ainda não há webhooks de mudanças recentes para este servidor.",
         "name": "Nome do webhook:",
         "new_lang": "<novo idioma>",
         "new_wiki": "<link para a wiki>",
-        "no_feeds": "a wiki deste webhook não possui recursos baseados em feeds, como discussões, murais de mensagens ou comentários de artigos, ativados.",
-        "noadmin": "você precisa da permissão `Gerenciar Webhooks` para este comando!",
+        "no_feeds": "A wiki deste webhook não possui recursos baseados em feeds, como discussões, murais de mensagens ou comentários de artigos, ativados.",
+        "noadmin": "Você precisa da permissão `Gerenciar Webhooks` para este comando!",
         "rc": "Mudanças da wiki:",
-        "sysmessage": "a mensagem do sistema `$1` deve ser a identificação do servidor `$2` para adicionar um webhook de mudanças recentes.",
+        "sysmessage": "A mensagem do sistema `$1` deve ser a identificação do servidor `$2` para adicionar um webhook de mudanças recentes.",
         "title": "Mudanças recentes no webhook",
         "toggle": "(alternar)",
-        "updated_display": "o modo de exibição deste webhook foi alterado para:",
-        "updated_lang": "o idioma para este webhook foi alterado para:",
-        "updated_wiki": "a wiki para este webhook foi alterado para:",
+        "updated_display": "O modo de exibição deste webhook foi alterado para:",
+        "updated_lang": "O idioma para este webhook foi alterado para:",
+        "updated_wiki": "A wiki para este webhook foi alterado para:",
         "webhook": {
             "blocked": "Este webhook de mudanças recentes será excluído porque a wiki foi bloqueado!",
             "blocked_help": "Você pode pedir mais detalhes no [servidor de suporte]($1).",
@@ -574,10 +574,11 @@
             "updated_lang": "O idioma foi alterado para `$1` para este webhook de mudanças recentes.",
             "updated_wiki": "A wiki foi alterado para $1 para este webhook de mudanças recentes."
         },
-        "webhook_failed": "infelizmente, o webhook não pôde ser criado, tente novamente mais tarde.",
+        "webhook_failed": "Infelizmente, o webhook não pôde ser criado, tente novamente mais tarde.",
         "wiki": "Wiki:"
     },
     "search": {
+        "cached": "Última atualização do cache:",
         "category": {
             "content": "Conteúdo nessa categoria:",
             "empty": "*Esta categoria está vazia*",
@@ -595,14 +596,14 @@
     },
     "settings": {
         "button": "Use o painel de controle",
-        "channel current": "estas são as configurações atuais deste canal:",
-        "channel lang": "o idioma para este canal é:",
-        "channel langchanged": "você alterou o idioma deste canal para:",
-        "channel role": "o cargo mínimo necessário para este canal é:",
+        "channel current": "Estas são as configurações atuais deste canal:",
+        "channel lang": "O idioma para este canal é:",
+        "channel langchanged": "Vvocê alterou o idioma deste canal para:",
+        "channel role": "O cargo mínimo necessário para este canal é:",
         "channel rolechanged": "você alterou o cargo mínimo necessário para este canal para:",
-        "channel wiki": "a wiki padrão para este canal é:",
-        "channel wikichanged": "você mudou a wiki padrão para este canal para:",
-        "current": "estas são as configurações atuais para este servidor:",
+        "channel wiki": "A wiki padrão para este canal é:",
+        "channel wikichanged": "Você mudou a wiki padrão para este canal para:",
+        "current": "Estas são as configurações atuais para este servidor:",
         "currentchannel": "Substituições de canal:",
         "currentinline": "Comandos em linha:",
         "currentlang": "Idioma:",
@@ -617,50 +618,51 @@
         "foundwikis": "Você quer dizer algum dessas wikis?",
         "inline disabled": {
             "channel inline": "Os comandos embutidos estão atualmente desativados para este canal.",
-            "channel inlinechanged": "você desativou os comandos embutidos para este canal.",
+            "channel inlinechanged": "Você desativou os comandos embutidos para este canal.",
             "help": "Use `$1` para ativar comandos embutidos como `[[$2]]` e `{{$2}}`.",
             "inline": "Os comandos embutidos estão atualmente desativados para este servidor.",
-            "inlinechanged": "você desabilitou comandos embutidos para este servidor."
+            "inlinechanged": "Você desabilitou comandos embutidos para este servidor."
         },
         "inline enabled": {
-            "channel inline": "comandos embutidos estão atualmente ativados para este canal.",
-            "channel inlinechanged": "você ativou comandos embutidos para este canal.",
+            "channel inline": "Comandos embutidos estão atualmente ativados para este canal.",
+            "channel inlinechanged": "Você ativou comandos embutidos para este canal.",
             "help": "Use `$1` para desativar comandos embutidos como `[[$2]]` e `{{$2}}`.",
             "inline": "Os comandos embutidos estão atualmente ativados para este servidor.",
-            "inlinechanged": "você ativou comandos embutidos para este servidor."
+            "inlinechanged": "Você ativou comandos embutidos para este servidor."
         },
-        "lang": "o idioma para este servidor é:",
-        "langchanged": "você alterou o idioma deste servidor para:",
+        "lang": "O idioma para este servidor é:",
+        "langchanged": "Você alterou o idioma deste servidor para:",
         "langhelp": "Use `$1 <idioma>` para mudar o idioma.\nIdiomas atualmente suportados são:",
-        "langinvalid": "o idioma especificado não é suportado!",
-        "missing": "este servidor ainda não está configurado. Use $1 e $2 para alterar as configurações.",
+        "langinvalid": "O idioma especificado não é suportado!",
+        "missing": "Este servidor ainda não está configurado. Use $1 e $2 para alterar as configurações.",
         "nochannels": "*Nenhum canal sobrescreve ainda*",
-        "prefix": "o prefixo deste servidor é:",
-        "prefixchanged": "você mudou o prefixo deste servidor para:",
+        "prefix": "O prefixo deste servidor é:",
+        "prefixchanged": "Você mudou o prefixo deste servidor para:",
         "prefixhelp": "Use `$1 <prefix>` para alterar o prefixo.\nUse `_` no final para indicar um espaço no final do prefixo.\nO prefixo não pode incluir menções!",
-        "prefixinvalid": "o prefixo especificado não é suportado!",
-        "role": "o cargo mínimo necessário para este servidor é:",
-        "rolechanged": "você alterou o cargo mínimo necessário para este servidor para:",
+        "prefixinvalid": "O prefixo especificado não é suportado!",
+        "role": "O cargo mínimo necessário para este servidor é:",
+        "rolechanged": "Você alterou o cargo mínimo necessário para este servidor para:",
         "rolehelp": "Use `$1 <role>` para alterar o cargo mínimo necessário.",
-        "roleinvalid": "o cargo especificado não existe!",
-        "save_failed": "infelizmente as configurações não puderam ser salvas, por favor, tente novamente mais tarde.",
-        "wiki": "a wiki padrão para este servidor é:",
-        "wikichanged": "você mudou a wiki padrão para este servidor para:",
+        "roleinvalid": "O cargo especificado não existe!",
+        "save_failed": "Infelizmente as configurações não puderam ser salvas, por favor, tente novamente mais tarde.",
+        "wiki": "A wiki padrão para este servidor é:",
+        "wikichanged": "Você mudou a wiki padrão para este servidor para:",
         "wikihelp": "Use `$1 <link>` para alterar a wiki padrão.\nLink para um site MediaWiki como: `https://<wiki>.fandom.com/`",
-        "wikiinvalid": "por favor, forneça um link válido para um site MediaWiki , como Wikipédia ou Fandom!",
-        "wikiinvalid_http": "o site fornecido não tem um certificado TLS/SSL válido! Por razões de segurança, apenas wikis que usam HTTPS são suportados.\nSe você for um administrador de site, você pode obter um certificado de uma autoridade de certificação como *Let’s Encrypt*:\n<https://letsencrypt.org/getting-started/>",
-        "wikiinvalid_private": "a wiki fornecida é privada! Apenas wikis públicas que podem ser lidos por todos são suportados.",
-        "wikiinvalid_timeout": "o link fornecido demorou muito para responder!",
-        "wikimissing": "nenhuma wiki padrão está configurado para este servidor ainda!"
+        "wikiinvalid": "Por favor, forneça um link válido para um site MediaWiki , como Wikipédia ou Fandom!",
+        "wikiinvalid_http": "O site fornecido não tem um certificado TLS/SSL válido! Por razões de segurança, apenas wikis que usam HTTPS são suportados.\nSe você for um administrador de site, você pode obter um certificado de uma autoridade de certificação como *Let’s Encrypt*:\n<https://letsencrypt.org/getting-started/>",
+        "wikiinvalid_private": "A wiki fornecida é privada! Apenas wikis públicas que podem ser lidos por todos são suportados.",
+        "wikiinvalid_timeout": "O link fornecido demorou muito para responder!",
+        "wikimissing": "Nenhuma wiki padrão está configurado para este servidor ainda!"
     },
     "test": {
         "MediaWiki": "Requer pelo menos $1 para funcionalidade completa, encontrado `$2`.",
         "notice": "Funcionalidade limitada",
         "pause": "eu estou atualmente em pausa neste servidor.",
+        "slow": "Desculpe, eu estou meio lento neste momento.",
         "text": [
             "eu estou totalmente funcional!",
             "eu ainda estou vivo!",
-            "pode confiar, eu ainda estou vivo.",
+            "Pode confiar, eu ainda estou vivo.",
             "estou fazendo a diferença e ainda estou vivo.",
             "eu tô firme e forte e ainda estou vivo.",
             "eu nasci há dez mil anos atrás e ainda estou vivo.",
@@ -735,13 +737,13 @@
     "verification": {
         "accountage": "Idade da conta:",
         "add_more": "Para adicionar mais verificações:",
-        "added": "a verificação foi adicionada:",
+        "added": "A verificação foi adicionada:",
         "and": "e",
         "channel": "Canal:",
-        "channel_max": "muitos canais fornecidos.",
-        "channel_missing": "o canal fornecido não existe.",
-        "current": "estas são as verificações disponíveis para este servidor:",
-        "current_selected": "esta é a verificação `$1` para este servidor:",
+        "channel_max": "Muitos canais fornecidos.",
+        "channel_missing": "O canal fornecido não existe.",
+        "current": "Estas são as verificações disponíveis para este servidor:",
+        "current_selected": "Esta é a verificação `$1` para este servidor:",
         "dashboard": {
             "added": "$1 adicionou a verificação com id `$2`.",
             "added_notice": "$1 adicionou alguns avisos de verificação.",
@@ -750,7 +752,7 @@
             "updated_notice": "$1 atualizou alguns avisos de verificação."
         },
         "delete_current": "Para excluir esta verificação:",
-        "deleted": "a verificação foi excluída.",
+        "deleted": "A verificação foi excluída.",
         "disabled": "desativado",
         "editcount": "Contagem de edições:",
         "enabled": "ativado",
@@ -759,15 +761,15 @@
         "indays": "(em dias)",
         "logging": "Canal de registro:",
         "match": "Aviso de requisitos ausentes:",
-        "max_entries": "você já atingiu a quantidade máxima de verificações.",
-        "missing": "ainda não há verificações para este servidor.",
+        "max_entries": "Você já atingiu a quantidade máxima de verificações.",
+        "missing": "Ainda não há verificações para este servidor.",
         "new_accountage": "<nova idade da conta>",
         "new_channel": "<novo canal>",
         "new_editcount": "<nova contagem de edições>",
         "new_postcount": "<nova contagem de postagens>",
         "new_role": "<novo cargo>",
         "new_usergroup": "<novo grupo de usuários>",
-        "no_role": "por favor forneça um cargo para a nova verificação.",
+        "no_role": "Por favor forneça um cargo para a nova verificação.",
         "notice_embed": "Alguns avisos incluem links de marcação mascarados. Certifique-se de que o bot tenha as permissões `Embed Links` em todos os canais de verificação para que funcionem corretamente.",
         "or": "ou",
         "postcount": "Contagem de postagens (apenas wikis no Fandom):",
@@ -777,20 +779,20 @@
         "rename_no_permission": "**$1 não tem a permissão de `Gerenciar apelidos` para substituir o apelido pelos nomes de usuário da wiki!**",
         "role_add": "Cargo para adicionar:",
         "role_deleted": "**O cargo $1 parece não existir mais! **",
-        "role_managed": "o cargo fornecido não pode ser atribuído.",
-        "role_max": "muitos cargos fornecidos.",
-        "role_missing": "o cargo fornecido não existe.",
+        "role_managed": "O cargo fornecido não pode ser atribuído.",
+        "role_max": "Muitos cargos fornecidos.",
+        "role_missing": "O cargo fornecido não existe.",
         "role_none": "nenhum",
         "role_remove": "Cargo para remover:",
         "role_too_high": "**O cargo $1 é muito alto para o $2 atribuí-lo!**",
-        "save_failed": "infelizmente a verificação não pôde ser salva, tente novamente mais tarde.",
+        "save_failed": "Infelizmente a verificação não pôde ser salva, tente novamente mais tarde.",
         "success": "Aviso de sucesso:",
         "toggle": "(alternar)",
-        "updated": "a verificação foi atualizada:",
+        "updated": "A verificação foi atualizada:",
         "usergroup": "Grupo de usuários:",
-        "usergroup_max": "muitos grupos de usuários fornecidos.",
-        "usergroup_too_long": "o grupo de usuários fornecido é muito grande.",
-        "value_too_high": "o valor fornecido é muito alto."
+        "usergroup_max": "Muitos grupos de usuários fornecidos.",
+        "usergroup_too_long": "O grupo de usuários fornecido é muito grande.",
+        "value_too_high": "O valor fornecido é muito alto."
     },
     "verify": {
         "audit_reason": "Verificado como \"$1\"",
@@ -799,7 +801,7 @@
         "discord": "{{GENDER:$1|Usuário do Discord|Usuária do Discord}}:",
         "empty": "*vazio*",
         "error": "Ocorreu um erro ao verificar o usuário.",
-        "error_reply": "ocorreu um erro ao verificar o usuário, por favor tente novamente.",
+        "error_reply": "Ocorreu um erro ao verificar o usuário, por favor tente novamente.",
         "failed_gblock": "**Falha na verificação de bloqueios globais!**",
         "failed_rename": "**A alteração do apelido no Discord falhou!**",
         "failed_roles": "**A adição de cargos falhou!**",
@@ -809,12 +811,12 @@
         "help_guide": "Siga [esse guia]($1) para associar seu nome de usuário do Discord ao seu usuário na wiki:",
         "help_missing": "Certifique-se de que está usando seu nome de usuário da wiki e de que as maiúsculas e minúsculas estão corretas.",
         "help_subpage": "Adicione seu nome de usuário do Discord ($1) para sua subpágina do Discord na wiki:",
-        "missing": "não há verificações configuradas para este canal.",
+        "missing": "Não há verificações configuradas para este canal.",
         "notice": "Atenção:",
         "oauth_button": "Autenticar",
-        "oauth_message": "por favor use [este link]($1) para autenticar sua conta de wiki.",
+        "oauth_message": "Por favor use [este link]($1) para autenticar sua conta de wiki.",
         "oauth_message_dm": "Por favor, use este link para autenticar sua conta de wiki para $1.",
-        "oauth_private": "a wiki usa OAuth2 para verificação. Ative as mensagens diretas deste servidor ou use o comando `/verify` para que eu possa enviar um link de autenticação de forma privada.",
+        "oauth_private": "A wiki usa OAuth2 para verificação. Ative as mensagens diretas deste servidor ou use o comando `/verify` para que eu possa enviar um link de autenticação de forma privada.",
         "oauth_used": "*Verificado usando OAuth2 *",
         "qualified_add": "Adicionar para:",
         "qualified_add_error": "Não pode ser adicionado a:",
@@ -840,9 +842,9 @@
     "voice": {
         "channel": "Canal de voz",
         "disable": "Use `$1` desativar esta função.",
-        "disabled": "você desabilitou a função para adicionar cargos aos canais de voz.",
+        "disabled": "Você desabilitou a função para adicionar cargos aos canais de voz.",
         "enable": "Use `$1` para habilitar esta função.",
-        "enabled": "você ativou a função para adicionar cargos aos canais de voz.",
+        "enabled": "Você ativou a função para adicionar cargos aos canais de voz.",
         "join": "$1 juntou-se ao canal de voz \"$2\".",
         "left": "$1 deixou o canal de voz \"$2\".",
         "name": "nome do canal de voz",

+ 83 - 80
i18n/ru.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "ru-RU",
     "diff": {
-        "badrev": "отсутствует хотя бы одна из версий!",
+        "badrev": "Отсутствует хотя бы одна из версий!",
         "hidden": "*скрытая*",
         "info": {
             "added": "Добавлено:",
@@ -152,9 +152,9 @@
         "helpserver": "По вопросам и проблемам, пожалуйста, посетите мой сервер поддержки:",
         "limit": "🚨 **Остановитесь, вы достигли лимита!** 🚨\n\n$1, ваше сообщение содержит слишком много команд!",
         "missingperm": "У меня отсутствуют некоторые права для выполнения команды:",
-        "patreon": "эта функция для подписчиков Patreon!\nВы можете поддержать нас на Patreon для доступа к этой функции:",
-        "prefix": "префикс для этого сервера `$1`. Вы можете изменить префикс, введя команду `$1settings prefix`. Чтобы получить список всех команд, введите `$1help`.",
-        "readonly": "**база данных в текущий момент доступна только для чтения, изменение настроек невозможно!**"
+        "patreon": "Эта функция для подписчиков Patreon!\nВы можете поддержать нас на Patreon для доступа к этой функции:",
+        "prefix": "Префикс для этого сервера `$1`. Вы можете изменить префикс, введя команду `$1settings prefix`. Чтобы получить список всех команд, введите `$1help`.",
+        "readonly": "**База данных в текущий момент доступна только для чтения, изменение настроек невозможно!**"
     },
     "help": {
         "admin": "Эти команды могут выполнять только администраторы:",
@@ -389,7 +389,7 @@
                 "desc": "Я отвечу с ссылкой на соответствующую статью в выбранной Wikia вики: `https://<вики>.wikia.org/`"
             }
         },
-        "noadmin": "вам нужно разрешение `Управлять сервером` для этих команд!",
+        "noadmin": "Вам нужно разрешение `Управлять сервером` для этих команд!",
         "pause": "**Я в сейчас приостановлен на этом сервере!**\nМогут быть выполнены только эти команды:"
     },
     "interaction": {
@@ -490,34 +490,34 @@
     "rcscript": {
         "ad": "Хотите, чтобы свежие правки отображались напрямую в Discord? Используйте `$1rcscript` чтобы добавить к вашему серверу вебхук свежих правок, основанный на **$2**!",
         "add_more": "Создать ещё вебхуки свежих правок:",
-        "added": "вебхук свежих правок был добавлен для:",
-        "all_inactive": "нельзя одновременно отключить отслеживание и правок на вики, и изменений в лентах.",
+        "added": "Вебхук свежих правок был добавлен для:",
+        "all_inactive": "Нельзя одновременно отключить отслеживание и правок на вики, и изменений в лентах.",
         "audit_reason": "Вебхук свежих правок для \"$1\"",
         "audit_reason_delete": "Вебхук свежих правок удалён",
         "audit_reason_edit": "Вебхук свежих правок обновлён",
         "avatar": "Аватар вебхука:",
-        "blocked": "эта вики была блокирована от добавления к вебхуку свежих правок!",
-        "blocked_reason": "эта вики была блокирована от добавления к вебхуку свежих правок по причине: `$1`!",
+        "blocked": "Эта вики была блокирована от добавления к вебхуку свежих правок!",
+        "blocked_reason": "Эта вики была блокирована от добавления к вебхуку свежих правок по причине: `$1`!",
         "channel": "Канал:",
-        "current": "вебхуки свежих правок, существующие на данный момент на этом сервере:",
-        "current_display": "режим просмотра для этого вебхука:",
-        "current_lang": "язык этого вебхука:",
-        "current_selected": "вебхук свежих правок `$1`, существующий на данный момент на этом сервере:",
-        "current_wiki": "вики для этого вебхука:",
+        "current": "Вебхуки свежих правок, существующие на данный момент на этом сервере:",
+        "current_display": "Режим просмотра для этого вебхука:",
+        "current_lang": "Язык этого вебхука:",
+        "current_selected": "Вебхук свежих правок `$1`, существующий на данный момент на этом сервере:",
+        "current_wiki": "Вики для этого вебхука:",
         "dashboard": {
             "added": "$1 создал вебхук свежих правок с ID `$2`.",
             "removed": "$1 удалил вебхук свежих правок с ID `$2`.",
             "updated": "$1 изменил вебхук свежих правок с ID `$2`."
         },
         "delete": "Удалить этот вебхук свежих правок:",
-        "deleted": "вебхук свежих правок был удалён.",
+        "deleted": "Вебхук свежих правок был удалён.",
         "disabled": "отключен",
-        "disabled_feeds": "отслеживание изменений в лентах, таких как обсуждения, стены обсуждения и комментарии к статьям, было отключено для этого вебхука.",
-        "disabled_rc": "отслеживание правок на вики было отключено для этого вебхука.",
+        "disabled_feeds": "Отслеживание изменений в лентах, таких как обсуждения, стены обсуждения и комментарии к статьям, было отключено для этого вебхука.",
+        "disabled_rc": "Отслеживание правок на вики было отключено для этого вебхука.",
         "display": "Режим просмотра:",
         "enabled": "включено",
-        "enabled_feeds": "отслеживание изменений в лентах, таких как обсуждения, стены обсуждения и комментарии к статьям, было включено для этого вебхука.",
-        "enabled_rc": "отслеживание правок на вики было включено для этого вебхука.",
+        "enabled_feeds": "Отслеживание изменений в лентах, таких как обсуждения, стены обсуждения и комментарии к статьям, было включено для этого вебхука.",
+        "enabled_rc": "Отслеживание правок на вики было включено для этого вебхука.",
         "feeds": "Изменения в лентах:",
         "help_display_compact": "Компактные текстовые сообщения со встроенными ссылками.",
         "help_display_diff": "Встроенные сообщения с изображениями и отличиями между правками.",
@@ -527,20 +527,20 @@
         "help_lang": "Языки, поддерживаемые на данный момент:",
         "help_wiki": "Ссылка на сайт MediaWiki наподобие `https://<вики>.fandom.com/`",
         "lang": "Язык:",
-        "max_entries": "вы уже достигли максимального количества вебхуков свежих правок.",
-        "missing": "на этом сервере ещё нет вебхуков свежих правок.",
+        "max_entries": "Вы уже достигли максимального количества вебхуков свежих правок.",
+        "missing": "На этом сервере ещё нет вебхуков свежих правок.",
         "name": "Имя вебхука:",
         "new_lang": "<новый язык>",
         "new_wiki": "<ссылка на вики>",
-        "no_feeds": "на вики для этого вебхука не поддерживаются или не включены функции на основе лент, такие как обсуждения, стены обсуждения и комментарии к статьям.",
-        "noadmin": "вам необходимо разрешение `Управлять Вебхуками` для выполнения этой команды!",
+        "no_feeds": "На вики для этого вебхука не поддерживаются или не включены функции на основе лент, такие как обсуждения, стены обсуждения и комментарии к статьям.",
+        "noadmin": "Вам необходимо разрешение `Управлять Вебхуками` для выполнения этой команды!",
         "rc": "Правки на вики:",
-        "sysmessage": "системное сообщение `$1` должно быть таким же, как ID сервера `$2`, чтобы создать вебхук свежих правок.",
+        "sysmessage": "Системное сообщение `$1` должно быть таким же, как ID сервера `$2`, чтобы создать вебхук свежих правок.",
         "title": "Вебхук свежих правок",
         "toggle": "(переключить)",
-        "updated_display": "режим просмотра для этого вебхука был изменён на:",
-        "updated_lang": "язык этого вебхука был изменён на:",
-        "updated_wiki": "вики этого вебхука была изменена на:",
+        "updated_display": "Режим просмотра для этого вебхука был изменён на:",
+        "updated_lang": "Язык этого вебхука был изменён на:",
+        "updated_wiki": "Вики этого вебхука была изменена на:",
         "webhook": {
             "blocked": "Этот вебхук свежих правок будет удалён, потому что эта вики была блокирована от добавления к вебхуку свежих правок!",
             "blocked_help": "Вы можете обратиться с вопросами на [сервер поддержки]($1).",
@@ -574,7 +574,7 @@
             "updated_lang": "Язык для этого вебхука свежих правок был изменён на `$1`.",
             "updated_wiki": "Вики для этого вебхука свежих правок была изменена на $1."
         },
-        "webhook_failed": "к сожалению, вебхук не мог быть создан, пожалуйста попробуйте позже.",
+        "webhook_failed": "К сожалению, вебхук не мог быть создан, пожалуйста попробуйте позже.",
         "wiki": "Вики:"
     },
     "search": {
@@ -591,19 +591,21 @@
         "infosearch": "Неверный результат? Используйте $1 для прямых ссылок или $2 для получения всех результатов.",
         "loading": "Загружается описание страницы…",
         "media": "На страницу описания файла",
+        "messagedefault": "По Умолчанию:",
+        "messagedefaultnone": "*нет*",
         "results": "Всего $1 {{PLURAL:$2|результат|результата|результатов}}",
         "special": "Содержание этой служебной страницы:"
     },
     "settings": {
         "button": "Используйте Панель Управления",
-        "channel current": "вот настройки этого канала на данный момент:",
-        "channel lang": "язык этого канала на данный момент:",
-        "channel langchanged": "вы сменили язык этого канала на:",
-        "channel role": "минимальная роль для этого канала:",
-        "channel rolechanged": "вы изменили минимальную роль для этого канала на:",
-        "channel wiki": "вики по умолчанию для этого канала:",
-        "channel wikichanged": "вы изменили вики по умолчанию для этого канала на:",
-        "current": "вот настройки сервера на данный момент:",
+        "channel current": "Вот настройки этого канала на данный момент:",
+        "channel lang": "Язык этого канала на данный момент:",
+        "channel langchanged": "Вы сменили язык этого канала на:",
+        "channel role": "Минимальная роль для этого канала:",
+        "channel rolechanged": "Вы изменили минимальную роль для этого канала на:",
+        "channel wiki": "Вики по умолчанию для этого канала:",
+        "channel wikichanged": "Вы изменили вики по умолчанию для этого канала на:",
+        "current": "Вот настройки сервера на данный момент:",
         "currentchannel": "Перезаписи настроек каналов:",
         "currentinline": "Встроенные команды:",
         "currentlang": "Язык:",
@@ -617,51 +619,52 @@
         },
         "foundwikis": "Вы имеете в виду какие-либо из этих вики?",
         "inline disabled": {
-            "channel inline": "встроенные команды на данный момент отключены в этом канале.",
-            "channel inlinechanged": "вы отключили встроенные команды для этого канала.",
+            "channel inline": "Встроенные команды на данный момент отключены в этом канале.",
+            "channel inlinechanged": "Вы отключили встроенные команды для этого канала.",
             "help": "Используйте `$1` чтобы включить встроенные команды, такие как `[[$2]]` и `{{$2}}`.",
-            "inline": "встроенные команды на данный момент отключены на этом сервере.",
-            "inlinechanged": "вы отключили встроенные команды на этом сервере."
+            "inline": "Встроенные команды на данный момент отключены на этом сервере.",
+            "inlinechanged": "Вы отключили встроенные команды на этом сервере."
         },
         "inline enabled": {
-            "channel inline": "встроенные команды на данный момент включены в этом канале.",
-            "channel inlinechanged": "вы включили встроенные команды для этого канала.",
+            "channel inline": "Встроенные команды на данный момент включены в этом канале.",
+            "channel inlinechanged": "Вы включили встроенные команды для этого канала.",
             "help": "Используйте `$1` чтобы отключить встроенные команды, такие как `[[$2]]` и `{{$2}}`.",
-            "inline": "встроенные команды на данный момент включены на этом сервере.",
-            "inlinechanged": "вы включили встроенные команды на этом сервере."
+            "inline": "Встроенные команды на данный момент включены на этом сервере.",
+            "inlinechanged": "Вы включили встроенные команды на этом сервере."
         },
-        "lang": "язык этого сервера на данный момент:",
-        "langchanged": "вы сменили язык этого сервера на:",
+        "lang": "Язык этого сервера на данный момент:",
+        "langchanged": "Вы сменили язык этого сервера на:",
         "langhelp": "Используйте `$1 <язык>` для смены языка.\nВ настоящее время поддерживаются языки:",
-        "langinvalid": "указанный язык не поддерживается!",
-        "missing": "этот сервер еще не настроен. Используйте $1 и $2, чтобы изменить настройки.",
+        "langinvalid": "Указанный язык не поддерживается!",
+        "missing": "Этот сервер еще не настроен. Используйте $1 и $2, чтобы изменить настройки.",
         "nochannels": "*Перезаписей настроек канала не было*",
-        "prefix": "префикс для этого сервера:",
-        "prefixchanged": "вы изменили префикс для этого сервера на:",
+        "prefix": "Префикс для этого сервера:",
+        "prefixchanged": "Вы изменили префикс для этого сервера на:",
         "prefixhelp": "Используйте `$1 <префикс>`, чтобы изменить префикс.\nПоставьте `_` в конец, чтобы поставить пробел на конце префикса.\nПрефикс не может включать в себя упоминания!",
-        "prefixinvalid": "указанный префикс не поддерживается!",
-        "role": "минимальная роль для этого сервера:",
-        "rolechanged": "вы изменили минимальную роль для этого сервера на:",
+        "prefixinvalid": "Указанный префикс не поддерживается!",
+        "role": "Минимальная роль для этого сервера:",
+        "rolechanged": "Вы изменили минимальную роль для этого сервера на:",
         "rolehelp": "Используйте `$1 <роль>` чтобы изменить минимальную роль, необходимую для использования команд.",
-        "roleinvalid": "указанная роль не существует!",
-        "save_failed": "к сожалению, настройки не могут быть сохранены, пожалуйста попробуйте позже.",
-        "wiki": "вики по умолчанию для этого сервера:",
-        "wikichanged": "вы изменили вики по умолчанию для этого сервера:",
+        "roleinvalid": "Указанная роль не существует!",
+        "save_failed": "К сожалению, настройки не могли быть сохранены, пожалуйста попробуйте позже.",
+        "wiki": "Вики по умолчанию для этого сервера:",
+        "wikichanged": "Вы изменили вики по умолчанию для этого сервера:",
         "wikihelp": "Используйте `$1 <ссылка>` чтобы изменить вики по умолчанию.\nСсылка на сайт MediaWiki наподобие `https://<вики>.fandom.com/`",
-        "wikiinvalid": "предоставьте действительную ссылку на сайт MediaWiki, такой как Wikipedia или вики на Fandom!",
-        "wikiinvalid_http": "у данного сайта отсутствует действительный сертификат TLS/SSL! Для безопасности поддерживаются только те вики, которые используют HTTPS.\nЕсли вы — системный администратор сайта, вы можете получить сертификат от центра сертификации, такого как *Let’s Encrypt*:\n<https://letsencrypt.org/ru/getting-started/>",
-        "wikiinvalid_private": "эта вики приватная! Вики-Бот поддерживает только публичные вики (на которых у всех есть права на чтение).",
-        "wikiinvalid_timeout": "от этого сайта превышено время ожидания ответа!",
-        "wikimissing": "вики по умолчанию для этого сервера не установлены!"
+        "wikiinvalid": "Предоставьте действительную ссылку на сайт MediaWiki, такой как Wikipedia или вики на Fandom!",
+        "wikiinvalid_http": "У данного сайта отсутствует действительный сертификат TLS/SSL! Для безопасности поддерживаются только те вики, которые используют HTTPS.\nЕсли вы — системный администратор сайта, вы можете получить сертификат от центра сертификации, такого как *Let’s Encrypt*:\n<https://letsencrypt.org/ru/getting-started/>",
+        "wikiinvalid_private": "Эта вики приватная! Вики-Бот поддерживает только публичные вики (на которых у всех есть права на чтение).",
+        "wikiinvalid_timeout": "От этого сайта превышено время ожидания ответа!",
+        "wikimissing": "Вики по умолчанию для этого сервера не установлена!"
     },
     "test": {
         "MediaWiki": "Необходимо $1 или более для полной функциональности, у вас `$2`.",
         "notice": "Ограниченная функциональность",
         "pause": "В настоящее время я приостановлен на этом сервере.",
+        "slow": "Кажется, я сейчас немного медленный, приношу извинения.",
         "text": [
             "Я полностью работоспособен!",
             "Я всё ещё жив!",
-            "и поверь мне, я ещё жив.",
+            "И поверь мне, я ещё жив.",
             "Я занимаюсь наукой и всё ещё жив.",
             "Я чувствую себя фантастически, и я всё ещё жив.",
             " ",
@@ -736,13 +739,13 @@
     "verification": {
         "accountage": "Возраст аккаунта:",
         "add_more": "Добавить еще проверок:",
-        "added": "проверка была добавлена:",
+        "added": "Проверка была добавлена:",
         "and": "и",
         "channel": "Канал:",
         "channel_max": "Вы достигли максимального кол-ва каналов.",
-        "channel_missing": "такого канала не существует.",
-        "current": "текущие проверки для этого сервера:",
-        "current_selected": "это проверка `$1` для этого сервера:",
+        "channel_missing": "Такого канала не существует.",
+        "current": "Текущие проверки для этого сервера:",
+        "current_selected": "Это проверка `$1` для этого сервера:",
         "dashboard": {
             "added": "$1 добавил верификацию с ID `$2`.",
             "added_notice": "$1 добавил уведомления о верификации.",
@@ -751,7 +754,7 @@
             "updated_notice": "$1 обновил уведомления о верификации."
         },
         "delete_current": "Удалить проверку:",
-        "deleted": "проверка была удалена.",
+        "deleted": "Проверка была удалена.",
         "disabled": "отключено",
         "editcount": "Количество правок:",
         "enabled": "включено",
@@ -768,7 +771,7 @@
         "new_postcount": "<новое количество постов>",
         "new_role": "<новая роль>",
         "new_usergroup": "<новая группа пользователей>",
-        "no_role": "пожалуйста, укажите роль для новой проверки.",
+        "no_role": "Пожалуйста, укажите роль для новой проверки.",
         "notice_embed": "Некоторые уведомления содержат встроенные ссылки. Убедитесь в том, что у бота есть разрешение `Встраивать Ссылки` во всех каналах верификации, чтобы уведомления работали исправно.",
         "or": "или",
         "postcount": "Количество постов (только для вики на Fandom):",
@@ -778,20 +781,20 @@
         "rename_no_permission": "**$1 отсутствует разрешение `Управлять никнеймами` для принудительного использования имён участников вики!**",
         "role_add": "Добавить роль:",
         "role_deleted": "**Роль $1 больше не существует!**",
-        "role_managed": "данная роль не может быть назначена.",
+        "role_managed": "Данная роль не может быть назначена.",
         "role_max": "Вы достигли максимального кол-ва ролей.",
-        "role_missing": "такой роли не существует.",
+        "role_missing": "Такой роли не существует.",
         "role_none": "нет",
         "role_remove": "Убрать роль:",
         "role_too_high": "**Роль $1 слишком высока $2 для назначения!**",
-        "save_failed": "к сожалению, проверка не сохранена, повторите попытку позже.",
+        "save_failed": "К сожалению, проверка не сохранена, повторите попытку позже.",
         "success": "Уведомление об успехе:",
         "toggle": "(переключить)",
-        "updated": "проверка была обновлена:",
+        "updated": "Проверка была обновлена:",
         "usergroup": "Пользовательские группы:",
-        "usergroup_max": "вы установили слишком много пользовательских групп.",
-        "usergroup_too_long": "устанавливаемая пользовательская группа слишком длинная.",
-        "value_too_high": "устанавливаемое значение слишком высоко."
+        "usergroup_max": "Вы установили слишком много пользовательских групп.",
+        "usergroup_too_long": "Устанавливаемая пользовательская группа слишком длинная.",
+        "value_too_high": "Установленное вами значение слишком высоко."
     },
     "verify": {
         "audit_reason": "Проверено как \"$1\"",
@@ -800,7 +803,7 @@
         "discord": "Discord-{{GENDER:$1|пользователь|пользовательница|пользователь}}:",
         "empty": "*пусто*",
         "error": "Проверка не удалась из-за ошибки.",
-        "error_reply": "проверка не удалась из-за ошибки, попробуйте позже.",
+        "error_reply": "Проверка не удалась из-за ошибки, попробуйте позже.",
         "failed_gblock": "**Не удалось проверить глобальный бан!**",
         "failed_rename": "**Ошибка при попытке изменения никнейма!**",
         "failed_roles": "**Не удалось добавить роль!**",
@@ -810,12 +813,12 @@
         "help_guide": "Изучите [этот гайд]($1) для добавления вашего Discord-тега в свой вики-профиль:",
         "help_missing": "Убедитесь, что вы правильно ввели своё имя пользователя, и что регистр букв совпадает.",
         "help_subpage": "Пожалуйста добавьте своё имя пользователя в Discord ($1) на подстраницу \"Discord\" вашей страницы участника на вики:",
-        "missing": "для этого канала не настроены проверки.",
+        "missing": "Для этого канала не настроены проверки.",
         "notice": "Заметка:",
         "oauth_button": "Авторизуйтесь",
-        "oauth_message": "пройдите по [этой ссылке]($1), чтобы авторизовать свой вики-аккаунт.",
+        "oauth_message": "Пройдите по [этой ссылке]($1), чтобы авторизовать свой вики-аккаунт.",
         "oauth_message_dm": "Пройдите по этой ссылке, чтобы авторизовать свой вики-аккаунт для сервера $1.",
-        "oauth_private": "данная вики использует OAuth2 для верификации. Разрешите личные сообщения от участников сервера или используйте команду `/verify`, чтобы я смог приватно отправить вам ссылку для верификации.",
+        "oauth_private": "Данная вики использует OAuth2 для верификации. Разрешите личные сообщения от участников сервера или используйте команду `/verify`, чтобы я смог приватно отправить вам ссылку для верификации.",
         "oauth_used": "*Верифицирован с помощью OAuth2*",
         "qualified_add": "Добавлено:",
         "qualified_add_error": "Не может быть добавлено:",

+ 179 - 0
i18n/sr.json

@@ -0,0 +1,179 @@
+{
+    "__translator": [
+        "Fensokratica",
+        " ",
+        " ",
+        " ",
+        " ",
+        " ",
+        " ",
+        " ",
+        " ",
+        " "
+    ],
+    "aliases": {
+        "bug": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "command": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "diff": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "discussion": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "help": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "info": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "invite": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "overview": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "page": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "random": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "search": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "test": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "user": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ],
+        "verify": [
+            "",
+            " ",
+            " ",
+            " ",
+            " "
+        ]
+    },
+    "diff": {
+        "info": {
+            "added": "Dodato:",
+            "comment": "Komentar:",
+            "editor": "Urednik:",
+            "more": "I više",
+            "removed": "Uklonjeno:",
+            "size": "Razlika:",
+            "tags": "Oznake (Tagovi):",
+            "timestamp": "Uredi datum:",
+            "whitespace": "Samo razmak"
+        },
+        "nocomment": "*Nema opisa*"
+    },
+    "discussion": {
+        "image": "Prikaži Sliku",
+        "main": "Diskusije",
+        "post": "objava",
+        "tags": "Oznake (Tagovi):"
+    },
+    "fallback": [
+        "en",
+        " ",
+        " ",
+        " ",
+        " "
+    ],
+    "general": {
+        "database": "⚠️ **Ograničena Funkcionalnost** ⚠️\nNisu pronađena podešavanja, kontaktirajte vlasnika bota!",
+        "default": "Ovaj server još nije podešen. Koristite $1 ili kontrolnu tablu da promenite podešavanja."
+    },
+    "test": {
+        "text": [
+            "",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " ",
+            " "
+        ]
+    }
+}

+ 11 - 11
i18n/sv.json

@@ -778,7 +778,7 @@
         "audit_reason": "Verifierad som \"$1\"",
         "button_again": "Kolla igen",
         "button_wrong_user": "Du kan inte kontrollera den här verifieringen igen, det här är en verifiering på $1!",
-        "discord": "Discordanvändare:",
+        "discord": "Discord-användare:",
         "empty": "*tom*",
         "error": "Verifikationen misslyckades på grund av ett felmeddelande.",
         "error_reply": "verifikationen misslyckades på grund av ett felmeddelande, vänligen försök igen.",
@@ -803,20 +803,20 @@
         "qualified_remove": "Borttagen från:",
         "qualified_remove_error": "Kan inte tas bort från:",
         "user_blocked": "**Wiki-användaren $1 är blockerad!**",
-        "user_blocked_reply": "din länkade Wiki-användare **\"$1\" är blockerad!**",
+        "user_blocked_reply": "din länkade wiki-användare **\"$1\" är blockerad!**",
         "user_disabled": "**Wiki-kontot $1 är inaktiverat!**",
         "user_disabled_reply": "ditt länkade Wiki-konto **\"$1\" är inaktiverat!**",
-        "user_failed": "Discord användaren $1 stämmer inte överens med Wiki-användaren $2.",
-        "user_failed_reply": "din Discord tagg stämmer inte överens med Wiki-användaren \"$1\".",
+        "user_failed": "Discord-användaren $1 stämmer inte överens med wiki-användaren $2.",
+        "user_failed_reply": "din Discord-tagg stämmer inte överens med wiki-användaren \"$1\".",
         "user_gblocked": "**Wiki-användaren $1 är globalt blockerad!**",
-        "user_gblocked_reply": "din länkade Wiki-användare **\"$1\" är globalt blockerad!**",
-        "user_matches": "Discord användaren $1 stämmer överens med Wiki-användaren $2, men uppfyller inte kraven för någon roll.",
-        "user_matches_reply": "din Discord tagg stämmer överens med wiki användaren \"$1\", men uppfyller inte kraven för någon roll.",
-        "user_missing": "Wiki användaren \"$1\" existerar inte.",
-        "user_missing_reply": "din länkade wiki användare \"$1\" existerar inte.",
+        "user_gblocked_reply": "din länkade wiki-användare **\"$1\" är globalt blockerad!**",
+        "user_matches": "Discord-användaren $1 stämmer överens med wiki-användaren $2, men uppfyller inte kraven för någon roll.",
+        "user_matches_reply": "din Discord-tagg stämmer överens med wiki-användaren \"$1\", men uppfyller inte kraven för någon roll.",
+        "user_missing": "Wiki-användaren \"$1\" existerar inte.",
+        "user_missing_reply": "din länkade wiki-användare \"$1\" existerar inte.",
         "user_renamed": "{{GENDER:$1|Hans|Hennes|Hens}} Discord-smeknamn har ändrats till {{GENDER:$1|hans|hennes|hens}} wiki-användarnamn.",
-        "user_verified": "Discord användaren $1 har verifierats som wiki användaren $2.",
-        "user_verified_reply": "du har verifierats som wiki användaren \"$1\".",
+        "user_verified": "Discord-användaren $1 har verifierats som wiki-användaren $2.",
+        "user_verified_reply": "du har verifierats som wiki-användaren \"$1\".",
         "wiki": "Wiki-användare:"
     },
     "voice": {

+ 88 - 86
i18n/tr.json

@@ -113,7 +113,7 @@
     },
     "dateformat": "tr-TR",
     "diff": {
-        "badrev": "en az bir revizyon mevcut değil!",
+        "badrev": "En az bir revizyon mevcut değil!",
         "hidden": "*gizli*",
         "info": {
             "added": "Eklendi:",
@@ -146,15 +146,15 @@
     ],
     "general": {
         "database": "⚠️ **Kısıtlı İşlevsellik** ⚠️\nAyar bulunamadı. Lütfen bot sahibi ile iletişim geç!",
-        "default": "bu sunucu daha kurulmamış. $1 ile ya da kontrol panelini kullanarak ayarları değiştirin.",
-        "disclaimer": "Ben, Vikipedi ve Fandom vikileri gibi MediaWiki sitelerine kolayca bağlantı vermek ve arama yapmak ile görevlendirilmiş küçük bir botum. Sayfalar için kısa açıklamalar ve ek bilgiler verebilir ve interwiki bağlantıları ile yönlendirmeleri takip edebilirim. $1, beni JavaScript kullanarak yazdı.\n\nBeni Patreon'da da destekleyebilirsiniz:",
+        "default": "Bu sunucu daha kurulmamış. $1 ile ya da kontrol panelini kullanarak ayarları değiştirin.",
+        "disclaimer": "Ben, Vikipedi ve Fandom vikileri gibi MediaWiki sitelerine kolayca bağlantı vermek ve arama yapmak ile görevlendirilmiş küçük bir botum. Sayfalar için kısa açıklamalar ve ek bilgiler verebilir ve interwiki bağlantıları ile yönlendirmeleri takip edebilirim. $1, beni JavaScript dilinde yazdı.\n\nBeni Patreon'da da destekleyebilirsiniz:",
         "experimental": "**Bu özellik deneme aşamasında! Çalışması garanti değil ve gelecekte kaldırılabilir.**",
         "helpserver": "Sorular ve sorunlarınız için lütfen destek sunucumu ziyaret edin:",
         "limit": "🚨 **Dur, limite ulaştın!** 🚨\n\n$1, mesajın çok fazla komut içeriyor!",
         "missingperm": "Bu komutu uygulamak için ihtiyacım olan birkaç izin eksik:",
         "patreon": "Bu, Patreon'a münhasır bir özellik!\nBu özelliğe erişim alabilmek için beni Patreon'da destekleyebilirsiniz:",
-        "prefix": "sunucudaki önekim `$1`. Bunu `$1settings prefix` ile değiştirebilirsin. Komutların bütün listesi için `$1yardım`.",
-        "readonly": "**veri tabanı şimdilik sadece okuma modunda, bu ayarları şu an değiştiremezsin!**"
+        "prefix": "Sunucudaki önekim `$1`. Bunu `$1settings prefix` ile değiştirebilirsin. Komutların bütün listesi için `$1yardım`.",
+        "readonly": "**Veri tabanı şimdilik sadece okuma modunda, bu ayarları şu an değiştiremezsin!**"
     },
     "help": {
         "admin": "Bu komutlar sadece yöneticiler tarafından kullanılabilir:",
@@ -490,34 +490,34 @@
     "rcscript": {
         "ad": "Son değişiklikleri Discord'unda görmek mi istiyorsun? `$1rcscript` kullanarak **$2** bazında son değişiklikler webhook'u ekleyebilirsin!",
         "add_more": "Daha fazla son değişiklikler webhook'u ekle:",
-        "added": "yeni bir son değişiklikler webhook'u eklendi:",
-        "all_inactive": "viki değişiklikleri ve yayın bazlı değişiklikleri aynı anda devre dışı bırakamazsın.",
+        "added": "Yeni bir son değişiklikler webhook'u eklendi:",
+        "all_inactive": "Viki değişiklikleri ve yayın bazlı değişiklikleri aynı anda devre dışı bırakamazsın.",
         "audit_reason": "\"$1\" için son değişiklikler webhook'u",
         "audit_reason_delete": "Son değişiklikler webhook'u kaldırıldı",
         "audit_reason_edit": "Son değişiklikler webhook'u güncellendi",
         "avatar": "Webhook simgesi:",
-        "blocked": "bu vikinin son değişiklikler webhook'u olarak eklemesi yasaklanmış!",
-        "blocked_reason": "bu vikinin son değişiklikler webhook'u olarak eklemesi `$1` yüzünden yasaklanmış!",
+        "blocked": "Bu vikinin son değişiklikler webhook'u olarak eklemesi yasaklanmış!",
+        "blocked_reason": "Bu vikinin son değişiklikler webhook'u olarak eklemesi `$1` yüzünden yasaklanmış!",
         "channel": "Kanal:",
-        "current": "bu sunucuda bulunan son değişiklikler webhook'ları:",
-        "current_display": "bu webhook'un görüntüleme modu:",
-        "current_lang": "bu webhook'un dili:",
-        "current_selected": "bu, sunucu için değişiklikler webhook'u `$1`:",
-        "current_wiki": "webhook vikisi:",
+        "current": "Bu sunucuda bulunan son değişiklikler webhook'ları:",
+        "current_display": "Bu webhook'un görüntüleme modu:",
+        "current_lang": "Bu webhook'un dili:",
+        "current_selected": "Bu, sunucu için değişiklikler webhook'u `$1`:",
+        "current_wiki": "Webhook vikisi:",
         "dashboard": {
             "added": "$1, `$2` tanımlayıcısıyla son değişiklikler webhook'u ekledi.",
             "removed": "$1, tanımlayıcısı `$2` olan son değişiklikler webhook'unu kaldırdı.",
             "updated": "$1, tanımlayıcısı `$2` olan son değişiklikler webhook'unu güncelledi."
         },
         "delete": "Bu son değişiklikler webhook'unu sil:",
-        "deleted": "bu son değişiklikler webhook'u silindi.",
+        "deleted": "Bu son değişiklikler webhook'u silindi.",
         "disabled": "devre dışı",
-        "disabled_feeds": "tartışmalar, mesaj duvarları ve makale komutları gibi yayın bazlı düzenlemeler bu webhook için devre dışı bırakıldı.",
-        "disabled_rc": "viki düzenlemeleri bu webhook için devre dışı bırakıldı.",
+        "disabled_feeds": "Tartışmalar, mesaj duvarları ve makale komutları gibi yayın bazlı düzenlemeler bu webhook için devre dışı bırakıldı.",
+        "disabled_rc": "Viki düzenlemeleri bu webhook için devre dışı bırakıldı.",
         "display": "Görüntüleme modu:",
         "enabled": "etkin",
-        "enabled_feeds": "tartışmalar, mesaj duvarları ve makale komutları gibi yayın bazlı düzenlemeler bu webhook için etkinleştirildi.",
-        "enabled_rc": "viki düzenlemeleri bu webhook için etkinleştirildi.",
+        "enabled_feeds": "Tartışmalar, mesaj duvarları ve makale komutları gibi yayın bazlı düzenlemeler bu webhook için etkinleştirildi.",
+        "enabled_rc": "Viki düzenlemeleri bu webhook için etkinleştirildi.",
         "feeds": "Yayın bazlı düzenlemeler:",
         "help_display_compact": "Satır içi bağlantılar ile kompakt metin mesajları.",
         "help_display_diff": "Görüntü ön izlemeleri ve düzenleme farklılıkları ile gömülü mesajlar.",
@@ -527,20 +527,20 @@
         "help_lang": "Halihazırda desteklenen diller:",
         "help_wiki": "`https://<wiki>.fandom.com/` gibi bir MediaWiki sitesine bağlantı ver",
         "lang": "Dil:",
-        "max_entries": "azami son değişiklikler webhook'u sayısına ulaştın.",
-        "missing": "bunu sunucuda halihazırda bir son değişiklikler webhook'u yok.",
+        "max_entries": "Azami son değişiklikler webhook'u sayısına ulaştın.",
+        "missing": "Bunu sunucuda halihazırda bir son değişiklikler webhook'u yok.",
         "name": "Webhook adı:",
         "new_lang": "<yeni dil>",
         "new_wiki": "<vikiye bağlantı>",
-        "no_feeds": "bu webhook'un vikide tartışmalar, mesaj duvarları ya da makale yorumları gibi yayın bazlı özellikler etkin değil.",
-        "noadmin": "bu komut için `Webhook'ları Yönet` iznine ihtiyacın var!",
+        "no_feeds": "Bu webhook'un vikide tartışmalar, mesaj duvarları ya da makale yorumları gibi yayın bazlı özellikler etkin değil.",
+        "noadmin": "Bu komut için `Webhook'ları Yönet` iznine ihtiyacın var!",
         "rc": "Viki düzenlemeleri:",
-        "sysmessage": "son değişiklikler webhook'u ekleyebilmek için sistem mesajı `$1`, sunucu ID'si `$2` olmalı.",
+        "sysmessage": "Son değişiklikler webhook'u ekleyebilmek için sistem mesajı `$1`, sunucu ID'si `$2` olmalı.",
         "title": "Son değişiklikler webhook'u",
         "toggle": "(değiştir)",
-        "updated_display": "bu webhook'un görüntüleme modu değiştirildi:",
-        "updated_lang": "bu webhook'un dili değiştirildi:",
-        "updated_wiki": "bu webhook'un vikisi değiştirildi:",
+        "updated_display": "Bu webhook'un görüntüleme modu değiştirildi:",
+        "updated_lang": "Bu webhook'un dili değiştirildi:",
+        "updated_wiki": "Bu webhook'un vikisi değiştirildi:",
         "webhook": {
             "blocked": "Bu son değişiklikler webhook'u silinecek çünkü viki engellendi!",
             "blocked_help": "[Destek suncusunda]($1) daha fazla detay alabilirsin.",
@@ -574,10 +574,11 @@
             "updated_lang": "Bu son değişiklikler webhook'unun dili `$1` ile değiştirildi.",
             "updated_wiki": "Bu son değişiklikler webhook'unun vikisi $1 ile değiştirildi."
         },
-        "webhook_failed": "ne yazık ki webhook oluşturulamadı, lütfen daha sonra tekrar deneyin.",
+        "webhook_failed": "Ne yazık ki webhook oluşturulamadı, lütfen daha sonra tekrar deneyin.",
         "wiki": "Viki:"
     },
     "search": {
+        "cached": "Son önbellek güncellemesi:",
         "category": {
             "content": "Bu kategorinin içeriği:",
             "empty": "*Bu kategori boş*",
@@ -595,14 +596,14 @@
     },
     "settings": {
         "button": "Kontrol panelini kullan",
-        "channel current": "kanalın mevcut ayarları:",
-        "channel lang": "bu kanalın dili:",
-        "channel langchanged": "kanalın dilini şuna değiştirdin:",
-        "channel role": "bu kanal için gerekli minimal rol:",
-        "channel rolechanged": "bu kanal için gerekli minimal rol değiştirildi:",
-        "channel wiki": "bu kanalın varsayılan wikisi:",
-        "channel wikichanged": "bu kanalın varsayılan wikisini şuna değiştirdiniz:",
-        "current": "sunucunun mevcut ayarları:",
+        "channel current": "Kanalın mevcut ayarları:",
+        "channel lang": "Bu kanalın dili:",
+        "channel langchanged": "Kanalın dilini şuna değiştirdin:",
+        "channel role": "Bu kanal için gerekli minimal rol:",
+        "channel rolechanged": "Bu kanal için gerekli minimal rol değiştirildi:",
+        "channel wiki": "Bu kanalın varsayılan wikisi:",
+        "channel wikichanged": "Bu kanalın varsayılan wikisini şuna değiştirdiniz:",
+        "current": "Sunucunun mevcut ayarları:",
         "currentchannel": "Kanal özel ayarları:",
         "currentinline": "Satır içi komutlar:",
         "currentlang": "Dil:",
@@ -616,51 +617,52 @@
         },
         "foundwikis": "Bu wikilerden herhangi birini mi kastediyorsun?",
         "inline disabled": {
-            "channel inline": "bu kanalda satır içi komutlar devre dışı.",
-            "channel inlinechanged": "bu kanal için satır içi komutları devre dışı bıraktın.",
+            "channel inline": "Bu kanalda satır içi komutlar devre dışı.",
+            "channel inlinechanged": "Bu kanal için satır içi komutları devre dışı bıraktın.",
             "help": "`[[$2]]` ve`{{$2}}` gibi satır içi komutları etkinleştirmek için `$1` kullan.",
-            "inline": "bu sunucuda satır içi komutlar devre dışı.",
-            "inlinechanged": "bu sunucu için satır içi komutları devre dışı bıraktın."
+            "inline": "Bu sunucuda satır içi komutlar devre dışı.",
+            "inlinechanged": "Bu sunucu için satır içi komutları devre dışı bıraktın."
         },
         "inline enabled": {
-            "channel inline": "bu kanal için satır içi komutlar etkin.",
-            "channel inlinechanged": "bu kanal için satır içi komutları etkinleştirdin.",
+            "channel inline": "Bu kanal için satır içi komutlar etkin.",
+            "channel inlinechanged": "Bu kanal için satır içi komutları etkinleştirdin.",
             "help": "`[[$2]]` ve `{{$2}}` gibi satır içi komutları devre dışı bırakmak için `$1` kullan.",
-            "inline": "bu sunucu için satır içi komutlar etkin.",
-            "inlinechanged": "bu sunucu için satır içi komutları etkinleştirdin."
+            "inline": "Bu sunucu için satır içi komutlar etkin.",
+            "inlinechanged": "Bu sunucu için satır içi komutları etkinleştirdin."
         },
-        "lang": "bu sunucunun dili:",
-        "langchanged": "sunucunun dilini şuna değiştirdin:",
+        "lang": "Bu sunucunun dili:",
+        "langchanged": "Sunucunun dilini şuna değiştirdin:",
         "langhelp": "`$1 <dil>` kullanarak dili değiştirebilirsin.\nŞimdilik desteklenmeyen diller:",
-        "langinvalid": "belirtilen dil şimdilik desteklenmiyor!",
-        "missing": "bu sunucu henüz kurulmamış. $1 ve $2'yi kullanarak ayarları değiştirin.",
+        "langinvalid": "Belirtilen dil şimdilik desteklenmiyor!",
+        "missing": "Bu sunucu henüz kurulmamış. $1 ve $2'yi kullanarak ayarları değiştirin.",
         "nochannels": "*Şimdilik bir kanala özel ayar yok*",
-        "prefix": "sunucudaki önekim:",
-        "prefixchanged": "sunucunun öneki şuna değiştirildi:",
+        "prefix": "Bunucudaki önekim:",
+        "prefixchanged": "Sunucunun öneki şuna değiştirildi:",
         "prefixhelp": "Öneki değiştirmek için `$1 <önek>` kullan.\nÖnekin sonunda bir boşluk belirtmek için `_` kullan.\nÖnekte bahsetme kullanamazsın!",
-        "prefixinvalid": "belirtilen önek desteklenmiyor!",
-        "role": "bu sunucu için gerekli minimal rol:",
-        "rolechanged": "bu sunucu için gerekli minimal rolü değiştirdin:",
+        "prefixinvalid": "Belirtilen önek desteklenmiyor!",
+        "role": "Bu sunucu için gerekli minimal rol:",
+        "rolechanged": "Bu sunucu için gerekli minimal rolü değiştirdin:",
         "rolehelp": "Gerekli minimal rolü değiştirmek için `$1 <rol>` kullan.",
-        "roleinvalid": "belirtilen rol mevcut değil!",
-        "save_failed": "ne yazık ki ayarlar kaydedilemedi, lütfen daha sonra tekrar deneyin.",
-        "wiki": "bu sunucunun varsayılan wikisi:",
-        "wikichanged": "bu sunucunun varsayılan wikisini şuna değiştirdiniz:",
+        "roleinvalid": "Belirtilen rol mevcut değil!",
+        "save_failed": "Ne yazık ki ayarlar kaydedilemedi, lütfen daha sonra tekrar deneyin.",
+        "wiki": "Bu sunucunun varsayılan wikisi:",
+        "wikichanged": "Bu sunucunun varsayılan wikisini şuna değiştirdiniz:",
         "wikihelp": "`$1 <wiki>` kullanarak varsayılan vikiyi değiştirebilirsin.\n`https://<wiki>.fandom.com/` gibi bir MediaWiki sitesine bağlantı ver",
-        "wikiinvalid": "lütfen Vikipedi veya bir Fandom vikisinin geçerli bağlantısını verin!",
-        "wikiinvalid_http": "belirtilen site geçerli bir TLS/SSL sertifikasına sahip değil! Güvenlik amacıyla sadece HTTPS kullanan vikiler desteklenmektedir.\nEğer bir site yöneticisiysen *Let’s Encrypt* gibi bir sertifika yetkilisinden sertifika alabilirsin:\n<https://letsencrypt.org/getting-started/>",
-        "wikiinvalid_private": "belirtilen viki gizli! Sadece herkes tarafından okunabilen halka açık vikiler desteklenmektedir.",
-        "wikiinvalid_timeout": "belirtilen bağlantının cevap vermesi çok uzun sürdü!",
-        "wikimissing": "bu sunucu için varsayılan bir wiki belirlenmemiş!"
+        "wikiinvalid": "Lütfen Vikipedi veya bir Fandom vikisinin geçerli bağlantısını verin!",
+        "wikiinvalid_http": "Belirtilen site geçerli bir TLS/SSL sertifikasına sahip değil! Güvenlik amacıyla sadece HTTPS kullanan vikiler desteklenmektedir.\nEğer bir site yöneticisiysen *Let’s Encrypt* gibi bir sertifika yetkilisinden sertifika alabilirsin:\n<https://letsencrypt.org/getting-started/>",
+        "wikiinvalid_private": "Belirtilen viki gizli! Sadece herkes tarafından okunabilen halka açık vikiler desteklenmektedir.",
+        "wikiinvalid_timeout": "Belirtilen bağlantının cevap vermesi çok uzun sürdü!",
+        "wikimissing": "Bu sunucu için varsayılan bir wiki belirlenmemiş!"
     },
     "test": {
         "MediaWiki": "Tam işlevsellik için $1 gerekli, bulunan `$2`.",
         "notice": "Sınırlı işlevsellik",
         "pause": "Şu anda bu sunucuda duraklatılmış durumdayım.",
+        "slow": "Pardon, biraz yavaşlamış durumdayım.",
         "text": [
             "Tamamen işlevselim!",
             "Hala hayattayım!",
-            "ve inan bana, ben hala hayattayım.",
+            "Ve inan bana, ben hala hayattayım.",
             "Bilim ile uğraşıyorum ve hala hayattayım.",
             "Kendimi harika hissediyorum ve hala hayattayım.",
             " ",
@@ -735,13 +737,13 @@
     "verification": {
         "accountage": "Hesap yaşı:",
         "add_more": "Daha fazla doğrulama ekle:",
-        "added": "doğrulama eklendi:",
+        "added": "Doğrulama eklendi:",
         "and": "ve",
         "channel": "Kanal:",
-        "channel_max": "çok fazla kanal belirttin.",
-        "channel_missing": "belirtilen kanal mevcut değil.",
-        "current": "sunucuda halihazırda bulunan doğrulamalar:",
-        "current_selected": "sunucu doğrulaması `$1`:",
+        "channel_max": "Çok fazla kanal belirttin.",
+        "channel_missing": "Belirtilen kanal mevcut değil.",
+        "current": "Sunucuda halihazırda bulunan doğrulamalar:",
+        "current_selected": "Sunucu doğrulaması `$1`:",
         "dashboard": {
             "added": "$1, `$2` ID'si ile doğrulama ekledi.",
             "added_notice": "$1 birkaç doğrulama bildirisi ekledi.",
@@ -750,7 +752,7 @@
             "updated_notice": "$1 birkaç doğrulama bildirisini güncelledi."
         },
         "delete_current": "Bu doğrulamayı sil:",
-        "deleted": "doğrulama silindi.",
+        "deleted": "Doğrulama silindi.",
         "disabled": "devre dışı",
         "editcount": "Düzenleme sayısı:",
         "enabled": "etkin",
@@ -759,15 +761,15 @@
         "indays": "(gün)",
         "logging": "Günlük kanalı:",
         "match": "Eksik gereklilik bildirisi:",
-        "max_entries": "halihazırda azami doğrulama sayısına sahipsin.",
-        "missing": "bu sunucuda halihazırda bir doğrulama yok.",
+        "max_entries": "Halihazırda azami doğrulama sayısına sahipsin.",
+        "missing": "Bu sunucuda halihazırda bir doğrulama yok.",
         "new_accountage": "<yeni hesap yaşı>",
         "new_channel": "<yeni kanal>",
         "new_editcount": "<yeni düzenleme sayısı>",
         "new_postcount": "<yeni gönderi sayısı>",
         "new_role": "<yeni rol>",
         "new_usergroup": "<yeni kullanıcı grubu>",
-        "no_role": "lütfen yeni doğrulama için bir rol belirtin.",
+        "no_role": "Lütfen yeni doğrulama için bir rol belirtin.",
         "notice_embed": "Bazı bildiriler maskeli biçimlendirme bağlantılarına sahip. Lütfen çalışabilmeleri için botun bütün doğrulama kanallarında `Bağlantı Yerleştir` yetkisine sahip olduğundan emin ol.",
         "or": "ya da",
         "postcount": "Gönderi sayısı (sadece Fandom vikileri):",
@@ -777,20 +779,20 @@
         "rename_no_permission": "**$1, viki kullanıcı isimlerini zorlamak için `Kullanıcı Adlarını Yönet`iznine sahip olmalı!**",
         "role_add": "Eklenecek rol:",
         "role_deleted": "**$1 rolü artık yok gibi duruyor!**",
-        "role_managed": "belirtilen rol atanamıyor.",
-        "role_max": "çok fazla rol belirttin.",
-        "role_missing": "belirtilen rol mevcut değil.",
+        "role_managed": "Belirtilen rol atanamıyor.",
+        "role_max": "Çok fazla rol belirttin.",
+        "role_missing": "Belirtilen rol mevcut değil.",
         "role_none": "hiçbiri",
         "role_remove": "Kaldırılacak rol:",
         "role_too_high": "**$1 rolü $2 tarafından atanmak için çok yüksekte!**",
-        "save_failed": "ne yazık ki doğrulama kaydedilemedi, lütfen daha sonra tekrar deneyin.",
+        "save_failed": "Ne yazık ki doğrulama kaydedilemedi, lütfen daha sonra tekrar deneyin.",
         "success": "Başarı bildirisi:",
         "toggle": "(değiştir)",
-        "updated": "doğrulama güncellendi:",
+        "updated": "Doğrulama güncellendi:",
         "usergroup": "Kullanıcı grubu:",
-        "usergroup_max": "çok fazla kullanıcı grubu belirttin.",
-        "usergroup_too_long": "belirtilen kullanıcı grubu çok uzun.",
-        "value_too_high": "belirtilen değer çok yüksek."
+        "usergroup_max": "Çok fazla kullanıcı grubu belirttin.",
+        "usergroup_too_long": "Belirtilen kullanıcı grubu çok uzun.",
+        "value_too_high": "Belirtilen değer çok yüksek."
     },
     "verify": {
         "audit_reason": "\"$1\" olarak doğrulandı",
@@ -799,7 +801,7 @@
         "discord": "Discord kullanıcısı:",
         "empty": "*boş*",
         "error": "Doğrulama bir hata sonucu başarısız oldu.",
-        "error_reply": "doğrulama bir hata sonucu başarısız oldu, lütfen tekrar deneyin.",
+        "error_reply": "Doğrulama bir hata sonucu başarısız oldu, lütfen tekrar deneyin.",
         "failed_gblock": "**Global engel kontrolü başarısız oldu!**",
         "failed_rename": "**Discord kullanıcı adı değiştirme başarısız oldu!**",
         "failed_roles": "**Rol eklenemedi!**",
@@ -809,12 +811,12 @@
         "help_guide": "Viki profiline Discord etiketi eklemek için [bu rehberi]($1) oku:",
         "help_missing": "Lütfen viki kullanıcı adını ve büyük küçük harf uyumunu doğru kullandığına emin ol.",
         "help_subpage": "Lütfen vikideki Discord alt sayfana Discord etiketini ($1) ekle:",
-        "missing": "kanalda halihazırda bir doğrulama yok.",
+        "missing": "Kanalda halihazırda bir doğrulama yok.",
         "notice": "Uyarı:",
         "oauth_button": "Doğrula",
-        "oauth_message": "lütfen [bu bağlantıyı]($1) kullanarak viki hesabını doğrula.",
+        "oauth_message": "Lütfen [bu bağlantıyı]($1) kullanarak viki hesabını doğrula.",
         "oauth_message_dm": "$1 sunucusu için viki hesabını doğrulamak için lütfen bu bağlantıyı kullan.",
-        "oauth_private": "bu viki doğrulama için OAuth2 kullanıyor. Lütfen sunucudan gelen direkt mesajları etkinleştir ve `/verify` komutunu kullan. Böylece doğrulama bağlantısını sana özel mesaj olarak gönderebilirim.",
+        "oauth_private": "Bu viki doğrulama için OAuth2 kullanıyor. Lütfen sunucudan gelen direkt mesajları etkinleştir ve `/verify` komutunu kullan. Böylece doğrulama bağlantısını sana özel mesaj olarak gönderebilirim.",
         "oauth_used": "*OAuth2 kullanılarak doğrulandı*",
         "qualified_add": "Eklendi:",
         "qualified_add_error": "Eklenemedi:",
@@ -840,9 +842,9 @@
     "voice": {
         "channel": "Ses kanalı",
         "disable": "Bu fonksiyou devre dışı bırakmak için `$1` komutunu kullanmalısın.",
-        "disabled": "sesli kanallara rol ekleme fonksiyonunu devre dışı bıraktın.",
+        "disabled": "Sesli kanallara rol ekleme fonksiyonunu devre dışı bıraktın.",
         "enable": "Bu fonksiyonu etkinleştirmek için `$1` komutunu kullanmalısın.",
-        "enabled": "sesli kanallara rol ekleme fonksiyonunu etkinleştirdin.",
+        "enabled": "Sesli kanallara rol ekleme fonksiyonunu etkinleştirdin.",
         "join": "$1 ses kanalına katıldı \"$2\".",
         "left": "$1 ses kanalından ayrıldı \"$2\".",
         "name": "ses kanalı adı",

BIN
i18n/widgets/bn.png


BIN
i18n/widgets/de.png


BIN
i18n/widgets/en.png


BIN
i18n/widgets/es.png


BIN
i18n/widgets/fr.png


BIN
i18n/widgets/hi.png


BIN
i18n/widgets/it.png


BIN
i18n/widgets/ja.png


BIN
i18n/widgets/ko.png


BIN
i18n/widgets/nl.png


BIN
i18n/widgets/pl.png


BIN
i18n/widgets/pt-br.png


BIN
i18n/widgets/ru.png


BIN
i18n/widgets/sr.png


BIN
i18n/widgets/sv.png


BIN
i18n/widgets/th.png


BIN
i18n/widgets/tr.png


BIN
i18n/widgets/uk.png


BIN
i18n/widgets/vi.png


BIN
i18n/widgets/zh-hans.png


BIN
i18n/widgets/zh-hant.png


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff