Markus-Rost 4 gadi atpakaļ
vecāks
revīzija
2b0a251795
11 mainītis faili ar 727 papildinājumiem un 5 dzēšanām
  1. 1 0
      .gitignore
  2. 7 0
      bot.js
  3. 32 0
      cmds/eval.js
  4. 53 0
      cmds/patreon.js
  5. 520 0
      cmds/rcscript.js
  6. 3 0
      functions/special_page.js
  7. 18 0
      i18n/allLangs.json
  8. 55 0
      i18n/en.json
  9. 1 1
      package.json
  10. 31 3
      util/database.js
  11. 6 1
      util/default.json

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 node_modules/
+rcgcdb/
 package-lock.json
 *.code-workspace
 *.env

+ 7 - 0
bot.js

@@ -363,6 +363,13 @@ client.on( 'guildDelete', guild => {
 		}
 		if ( this.changes ) console.log( '- Verifications successfully removed.' );
 	} );
+	db.run( 'DELETE FROM rcgcdw WHERE guild = ?', [guild.id], function (dberror) {
+		if ( dberror ) {
+			console.log( '- Error while removing the RcGcDw: ' + dberror );
+			return dberror;
+		}
+		if ( this.changes ) console.log( '- RcGcDw successfully removed.' );
+	} );
 } );
 
 

+ 32 - 0
cmds/eval.js

@@ -112,6 +112,30 @@ function removePatreons(guild, msg) {
 				console.log( '- Verifications successfully deleted.' );
 			} );
 		} );
+		db.all( 'SELECT webhook FROM rcgcdw WHERE guild = ? ORDER BY configid ASC', [guild], (dberror, rows) => {
+			if ( dberror ) {
+				console.log( '- Error while getting the RcGcDw: ' + dberror );
+				return dberror;
+			}
+			var webhooks = rows.slice(rcgcdwLimit.default).map( row => row.webhook );
+			if ( webhooks.length ) db.run( 'DELETE FROM rcgcdw WHERE webhook IN (' + webhooks.map( webhook => '?' ).join(', ') + ')', webhooks, function (error) {
+				if ( error ) {
+					console.log( '- Error while deleting the RcGcDw: ' + error );
+					return error;
+				}
+				console.log( '- RcGcDw successfully deleted.' );
+				webhooks.forEach( hook => guild.client.fetchWebhook(...hook.split('/')).then( webhook => {
+					webhook.delete('Removed extra recent changes webhook').catch(log_error);
+				}, log_error ) );
+			} );
+		} );
+		db.run( 'UPDATE rcgcdw SET display = ? WHERE guild = ? AND display > ?', [rcgcdwLimit.display, guild, rcgcdwLimit.display], function (dberror) {
+			if ( dberror ) {
+				console.log( '- Error while updating the RcGcDw: ' + dberror );
+				return dberror;
+			}
+			console.log( '- RcGcDw successfully updated.' );
+		} );
 	}
 	catch ( tryerror ) {
 		console.log( '- Error while removing the patreon features: ' + tryerror );
@@ -165,6 +189,14 @@ function removeSettings(msg) {
 						}
 						console.log( '- Verifications successfully removed.' );
 					} );
+					db.run( 'DELETE FROM rcgcdw WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
+						if ( dberror ) {
+							console.log( '- Error while removing the RcGcDw: ' + dberror );
+							msg.replyMsg( 'I got an error while removing the RcGcDw!', {}, true );
+							return dberror;
+						}
+						console.log( '- Verifications successfully removed.' );
+					} );
 				}
 				if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( channel => '?' ).join(', ') + ')', channels, function (dberror) {
 					if ( dberror ) {

+ 53 - 0
cmds/patreon.js

@@ -85,6 +85,30 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 					console.log( '- Verifications successfully deleted.' );
 				} );
 			} );
+			db.all( 'SELECT webhook FROM rcgcdw WHERE guild = ? ORDER BY configid ASC', [args[1]], (dberror, rows) => {
+				if ( dberror ) {
+					console.log( '- Error while getting the RcGcDw: ' + dberror );
+					return dberror;
+				}
+				var webhooks = rows.slice(rcgcdwLimit.default).map( row => row.webhook );
+				if ( webhooks.length ) db.run( 'DELETE FROM rcgcdw WHERE webhook IN (' + webhooks.map( webhook => '?' ).join(', ') + ')', webhooks, function (error) {
+					if ( error ) {
+						console.log( '- Error while deleting the RcGcDw: ' + error );
+						return error;
+					}
+					console.log( '- RcGcDw successfully deleted.' );
+					webhooks.forEach( hook => msg.client.fetchWebhook(...hook.split('/')).then( webhook => {
+						webhook.delete('Removed extra recent changes webhook').catch(log_error);
+					}, log_error ) );
+				} );
+			} );
+			db.run( 'UPDATE rcgcdw SET display = ? WHERE guild = ? AND display > ?', [rcgcdwLimit.display, args[1], rcgcdwLimit.display], function (dberror) {
+				if ( dberror ) {
+					console.log( '- Error while updating the RcGcDw: ' + dberror );
+					return dberror;
+				}
+				console.log( '- RcGcDw successfully updated.' );
+			} );
 		} );
 	} );
 	
@@ -194,6 +218,35 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 					return eacherror;
 				}
 			} );
+			db.each( 'SELECT GROUP_CONCAT(DISTINCT a.webhook) webhooks FROM rcgcdw a LEFT JOIN verification b ON a.guild = b.guild WHERE a.guild IN (' + guilds.map( guild => '?' ).join(', ') + ') GROUP BY a.guild', guilds, (eacherror, eachrow) => {
+				if ( eacherror ) {
+					console.log( '- Error while getting the RcGcDw: ' + eacherror );
+					return eacherror;
+				}
+				var webhooks = eachrow.webhooks.split(',').slice(rcgcdwLimit.default);
+				if ( webhooks.length ) db.run( 'DELETE FROM rcgcdw WHERE webhook IN (' + webhooks.map( webhook => '?' ).join(', ') + ')', webhooks, function (uperror) {
+					if ( uperror ) {
+						console.log( '- Error while deleting the RcGcDw: ' + uperror );
+						return uperror;
+					}
+					console.log( '- RcGcDw successfully deleted.' );
+					webhooks.forEach( hook => msg.client.fetchWebhook(...hook.split('/')).then( webhook => {
+						webhook.delete('Removed extra recent changes webhook').catch(log_error);
+					}, log_error ) );
+				} );
+			}, (eacherror) => {
+				if ( eacherror ) {
+					console.log( '- Error while getting the RcGcDw: ' + eacherror );
+					return eacherror;
+				}
+			} );
+			db.run( 'UPDATE rcgcdw SET display = ? WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ') AND display > ?', [rcgcdwLimit.display, ...guilds, rcgcdwLimit.display], function (uperror) {
+				if ( uperror ) {
+					console.log( '- Error while updating the RcGcDw: ' + uperror );
+					return uperror;
+				}
+				console.log( '- RcGcDw successfully updated.' );
+			} );
 		} );
 		if ( !row ) return db.run( 'INSERT INTO patreons(patreon, count) VALUES(?, ?)', [args[1], count], function (error) {
 			if ( error ) {

+ 520 - 0
cmds/rcscript.js

@@ -0,0 +1,520 @@
+const {limit: {rcgcdw: rcgcdwLimit}, defaultSettings, wikiProjects} = require('../util/default.json');
+const allLangs = require('../util/i18n.js').allLangs(true);
+var db = require('../util/database.js');
+
+const fs = require('fs');
+const rcscriptExists = fs.existsSync('./rcgcdb');
+
+var allSites = [];
+const getAllSites = require('../util/allSites.js');
+getAllSites.then( sites => allSites = sites );
+
+const display_types = [
+	'compact',
+	'embed',
+	'image',
+	'diff'
+];
+
+/**
+ * Processes the "rcscript" command.
+ * @param {import('../util/i18n.js')} 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 {String} wiki - The wiki for the message.
+ */
+function cmd_rcscript(lang, msg, args, line, wiki) {
+	if ( !msg.isAdmin() ) return msg.reactEmoji('❌');
+	// Patreon only during testing
+	if ( !( msg.guild.id in patreons ) ) {
+		return msg.replyMsg( lang.get('patreon') + '\n<' + process.env.patreon + '>', {}, true );
+	}
+	// Patreon only during testing
+	if ( !msg.channel.permissionsFor(msg.client.user).has('MANAGE_WEBHOOKS') ) {
+		console.log( msg.guild.id + ': Missing permissions - MANAGE_WEBHOOKS' );
+		return msg.replyMsg( lang.get('missingperm') + ' `MANAGE_WEBHOOKS`' );
+	}
+	
+	db.all( 'SELECT configid, webhook, wiki, lang, display, wikiid FROM rcgcdw WHERE guild = ? ORDER BY configid ASC', [msg.guild.id], (dberror, rows) => {
+		if ( dberror || !rows ) {
+			console.log( '- Error while getting the RcGcDw: ' + dberror );
+			msg.reactEmoji('error', true);
+			return dberror;
+		}
+
+		var prefix = process.env.prefix;
+		var limit = rcgcdwLimit.default;
+		var display = display_types.slice(0, rcgcdwLimit.display + 1);
+		if ( msg.guild.id in patreons ) {
+			prefix = patreons[msg.guild.id];
+			limit = rcgcdwLimit.patreon;
+			display = display_types.slice();
+		}
+
+		if ( args[0] === 'add' ) {
+			if ( !msg.channel.permissionsFor(msg.member).has('MANAGE_WEBHOOKS') ) {
+				return msg.replyMsg( lang.get('rcscript.noadmin') );
+			}
+			if ( rows.length >= limit ) return msg.replyMsg( lang.get('rcscript.max_entries'), {}, true );
+
+			var wikiinvalid = lang.get('settings.wikiinvalid') + '\n`' + prefix + 'rcscript add ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
+			var wikinew = args.slice(1).join(' ').toLowerCase().trim().replace( /^<\s*(.*?)\s*>$/, '$1' );
+			if ( !wikinew ) wikinew = wiki;
+			else {
+				wikinew = input_to_wiki(wikinew.replace( /^(?:https?:)?\/\//, 'https://' ));
+				if ( !wikinew ) return msg.replyMsg( wikiinvalid, {}, true );
+			}
+			return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw|recentchanges&amenableparser=true&siprop=general|extensions&titles=Special:RecentChanges&format=json', {
+				responseType: 'json'
+			} ).then( response => {
+				var body = response.body;
+				if ( response.statusCode !== 200 || !body?.query?.allmessages || !body?.query?.general || !body?.query?.extensions || !body?.query?.pages?.['-1'] ) {
+					console.log( '- ' + response.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
+					if ( reaction ) reaction.removeEmoji();
+					msg.reactEmoji('nowiki', true);
+					return msg.replyMsg( wikiinvalid, {}, true );
+				}
+				wikinew = body.query.general.server.replace( /^(?:https?:)?\/\//, 'https://' ) + body.query.general.scriptpath + '/';
+				if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) <= 30 ) {
+					console.log( '- This wiki is using ' + body.query.general.generator + '.' );
+					if ( reaction ) reaction.removeEmoji();
+					return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {}, true );
+				}
+				if ( body.query.allmessages[0]['*'] !== msg.guild.id ) {
+					if ( reaction ) reaction.removeEmoji();
+					return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit', '', body.query.general) + '>', {}, true );
+				}
+				if ( wikinew.isFandom() ) return got.get( 'https://community.fandom.com/api/v1/Wikis/ByString?includeDomain=true&limit=10&string=' + body.query.general.servername + body.query.general.scriptpath + '&format=json', {
+					responseType: 'json'
+				} ).then( wiresponse => {
+					var wibody = wiresponse.body;
+					if ( wiresponse.statusCode !== 200 || !wibody || wibody.exception || !wibody.items || !wibody.items.length ) {
+						console.log( '- ' + wiresponse.statusCode + ': Error while getting the wiki id: ' + wibody?.exception?.details );
+						return createWebhook();
+					}
+					var site = wibody.items.find( site => site.domain === body.query.general.servername + body.query.general.scriptpath );
+					if ( site ) return got.get( 'https://services.fandom.com/discussion/' + site.id + '/posts?limit=1&format=json', {
+						headers: {
+							Accept: 'application/hal+json'
+						},
+						responseType: 'json'
+					} ).then( dsresponse => {
+						var dsbody = dsresponse.body;
+						if ( dsresponse.statusCode !== 200 || !dsbody || dsbody.title ) {
+							if ( dsbody?.title !== 'site doesn\'t exists' ) console.log( '- ' + dsresponse.statusCode + ': Error while checking for discussions: ' + dsbody?.title );
+							return createWebhook();
+						}
+						return createWebhook(parseInt(site.id, 10));
+					}, error => {
+						console.log( '- Error while checking for discussions: ' + error );
+						return createWebhook();
+					} );
+					console.log( '- No result while getting the wiki id.' );
+					return createWebhook();
+				}, error => {
+					console.log( '- Error while getting the wiki id: ' + error );
+					return createWebhook();
+				} );
+				return createWebhook();
+
+				/**
+				 * Creates the webhook.
+				 * @param {Number} wikiid - The ID of the wiki.
+				 */
+				function createWebhook(wikiid = null) {
+					msg.channel.createWebhook( ( body.query.allmessages[1]['*'] || 'Recent changes' ), {
+						avatar: msg.client.user.displayAvatarURL({format:'png',size:4096}),
+						reason: lang.get('rcscript.audit_reason', wikinew)
+					} ).then( webhook => {
+						console.log( '- Webhook successfully created.' );
+						webhook.send( lang.get('rcscript.webhook.created', body.query.general.sitename) + '\n<' + wikinew.toLink(body.query.pages['-1'].title, '', '', body.query.general) + ( wikiid ? '>\n<' + wikinew + 'f' : '' ) + '>' ).catch(log_error);
+						var new_configid = 1;
+						for ( let i of rows.map( row => row.configid ) ) {
+							if ( new_configid === i ) new_configid++;
+							else break;
+						}
+						db.run( 'INSERT INTO rcgcdw(guild, configid, webhook, wiki, lang, display, wikiid) VALUES(?, ?, ?, ?, ?, ?, ?)', [msg.guild.id, new_configid, webhook.id + '/' + webhook.token, wikinew, ( allLangs.map[lang.lang] || defaultSettings.lang ), ( msg.showEmbed() ? 1 : 0 ), wikiid], function (error) {
+							if ( error ) {
+								console.log( '- Error while adding the RcGcDw: ' + error );
+								if ( reaction ) reaction.removeEmoji();
+								msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+								return error;
+							}
+							console.log( '- RcGcDw successfully added.' );
+							if ( reaction ) reaction.removeEmoji();
+							msg.replyMsg( lang.get('rcscript.added') + ' <' + wikinew + '>\n`' + prefix + 'rcscript' + ( rows.length ? ' ' + new_configid : '' ) + '`', {}, true );
+						} );
+					}, error => {
+						console.log( '- Error while creating the webhook: ' + error );
+						if ( reaction ) reaction.removeEmoji();
+						msg.replyMsg( lang.get('rcscript.webhook_failed'), {}, true );
+					} );
+				}
+			}, error => {
+				console.log( '- Error while testing the wiki: ' + error );
+				if ( reaction ) reaction.removeEmoji();
+				msg.reactEmoji('nowiki', true);
+				return msg.replyMsg( wikiinvalid, {}, true );
+			} ) );
+		}
+
+		var selected_row = rows.find( row => row.configid.toString() === args[0] );
+		if ( selected_row ) {
+			args[0] = args[1];
+			args[1] = args.slice(2).join(' ').toLowerCase().trim().replace( /^<\s*(.*)\s*>$/, '$1' );
+		}
+		else {
+			args[1] = args.slice(1).join(' ').toLowerCase().trim().replace( /^<\s*(.*)\s*>$/, '$1' );
+			if ( rows.length === 1 ) selected_row = rows[0];
+		}
+		if ( args[0] ) args[0] = args[0].toLowerCase();
+
+		if ( selected_row ) {
+			let cmd = prefix + 'rcscript' + ( rows.length === 1 ? '' : ' ' + selected_row.configid );
+
+			if ( args[0] === 'delete' && !args[1] ) {
+				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('MANAGE_WEBHOOKS') ) {
+						return msg.replyMsg( lang.get('rcscript.noadmin') );
+					}
+					webhook.send( lang.get('rcscript.webhook.deleted') ).catch(log_error);
+					db.run( 'DELETE FROM rcgcdw WHERE webhook = ?', [selected_row.webhook], function (delerror) {
+						if ( delerror ) {
+							console.log( '- Error while removing the RcGcDw: ' + delerror );
+							msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+							return delerror;
+						}
+						console.log( '- RcGcDw successfully removed.' );
+						msg.replyMsg( lang.get('rcscript.deleted'), {}, true );
+						webhook.delete(lang.get('rcscript.audit_reason_delete')).catch(log_error);
+					} );
+				}, error => {
+					log_error(error);
+					if ( error.toString() !== 'DiscordAPIError: Unknown Webhook' ) {
+						return msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+					}
+					db.run( 'DELETE FROM rcgcdw WHERE webhook = ?', [selected_row.webhook], function (delerror) {
+						if ( delerror ) {
+							console.log( '- Error while removing the RcGcDw: ' + delerror );
+							msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+							return delerror;
+						}
+						console.log( '- RcGcDw successfully removed.' );
+						msg.replyMsg( lang.get('rcscript.deleted'), {}, true );
+					} );
+				} );
+			}
+			if ( args[0] === 'wiki' ) {
+				if ( !args[1] ) {
+					return msg.replyMsg( lang.get('rcscript.current_wiki') + ' <' + selected_row.wiki + '>\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki'), {}, true );
+				}
+
+				var wikiinvalid = lang.get('settings.wikiinvalid') + '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n' + lang.get('rcscript.help_wiki');
+				var wikinew = input_to_wiki(args[1].replace( /^(?:https?:)?\/\//, 'https://' ));
+				if ( !wikinew ) return msg.replyMsg( wikiinvalid, {}, true );
+				return msg.reactEmoji('⏳', true).then( reaction => got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-RcGcDw&amenableparser=true&siprop=general|extensions&titles=Special:RecentChanges&format=json', {
+					responseType: 'json'
+				} ).then( response => {
+					var body = response.body;
+					if ( response.statusCode !== 200 || !body?.query?.allmessages || !body?.query?.general || !body?.query?.extensions || !body?.query?.pages?.['-1'] ) {
+						console.log( '- ' + response.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
+						if ( reaction ) reaction.removeEmoji();
+						msg.reactEmoji('nowiki', true);
+						return msg.replyMsg( wikiinvalid, {}, true );
+					}
+					wikinew = body.query.general.server.replace( /^(?:https?:)?\/\//, 'https://' ) + body.query.general.scriptpath + '/';
+					if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) <= 30 ) {
+						console.log( '- This wiki is using ' + body.query.general.generator + '.' );
+						if ( reaction ) reaction.removeEmoji();
+						return msg.replyMsg( lang.get('test.MediaWiki', 'MediaWiki 1.30', body.query.general.generator) + '\nhttps://www.mediawiki.org/wiki/MediaWiki_1.30', {}, true );
+					}
+					if ( body.query.allmessages[0]['*'] !== msg.guild.id ) {
+						if ( reaction ) reaction.removeEmoji();
+						return msg.replyMsg( lang.get('rcscript.sysmessage', 'MediaWiki:Custom-RcGcDw', msg.guild.id) + '\n<' + wikinew.toLink('MediaWiki:Custom-RcGcDw', 'action=edit', '', body.query.general) + '>', {}, true );
+					}
+					if ( wikinew.isFandom() ) return got.get( 'https://community.fandom.com/api/v1/Wikis/ByString?includeDomain=true&limit=10&string=' + body.query.general.servername + body.query.general.scriptpath + '&format=json', {
+						responseType: 'json'
+					} ).then( wiresponse => {
+						var wibody = wiresponse.body;
+						if ( wiresponse.statusCode !== 200 || !wibody || wibody.exception || !wibody.items || !wibody.items.length ) {
+							console.log( '- ' + wiresponse.statusCode + ': Error while getting the wiki id: ' + wibody?.exception?.details );
+							return updateWiki();
+						}
+						var site = wibody.items.find( site => site.domain === body.query.general.servername + body.query.general.scriptpath );
+						if ( site ) return got.get( 'https://services.fandom.com/discussion/' + site.id + '/posts?limit=1&format=json', {
+							headers: {
+								Accept: 'application/hal+json'
+							},
+							responseType: 'json'
+						} ).then( dsresponse => {
+							var dsbody = dsresponse.body;
+							if ( dsresponse.statusCode !== 200 || !dsbody || dsbody.title ) {
+								if ( dsbody?.title !== 'site doesn\'t exists' ) console.log( '- ' + dsresponse.statusCode + ': Error while checking for discussions: ' + dsbody?.title );
+								return updateWiki();
+							}
+							return updateWiki(parseInt(site.id, 10));
+						}, error => {
+							console.log( '- Error while checking for discussions: ' + error );
+							return updateWiki();
+						} );
+						console.log( '- No result while getting the wiki id.' );
+						return updateWiki();
+					}, error => {
+						console.log( '- Error while getting the wiki id: ' + error );
+						return updateWiki();
+					} );
+					return updateWiki();
+
+					/**
+					 * Changes the wiki.
+					 * @param {Number} wikiid - The ID of the wiki.
+					 */
+					function updateWiki(wikiid = null) {
+						msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+							webhook.send( lang.get('rcscript.webhook.updated_wiki', body.query.general.sitename) + '\n<' + wikinew.toLink(body.query.pages['-1'].title, '', '', body.query.general) + ( wikiid ? '>\n<' + wikinew + 'f' : '' ) + '>' ).catch(log_error);
+						}, log_error );
+						db.run( 'UPDATE rcgcdw SET wiki = ?, wikiid = ?, rcid = ?, postid = ? WHERE webhook = ?', [wikinew, wikiid, null, null, selected_row.webhook], function (error) {
+							if ( error ) {
+								console.log( '- Error while updating the RcGcDw: ' + error );
+								if ( reaction ) reaction.removeEmoji();
+								msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+								return error;
+							}
+							console.log( '- RcGcDw successfully updated.' );
+							if ( reaction ) reaction.removeEmoji();
+							msg.replyMsg( lang.get('rcscript.updated_wiki') + ' <' + wikinew + '>\n`' + cmd + '`', {}, true );
+						} );
+					}
+				}, error => {
+					console.log( '- Error while testing the wiki: ' + error );
+					if ( reaction ) reaction.removeEmoji();
+					msg.reactEmoji('nowiki', true);
+					return msg.replyMsg( wikiinvalid, {}, true );
+				} ) );
+			}
+			if ( args[0] === 'lang' ) {
+				if ( !args[1] ) {
+					return msg.replyMsg( lang.get('rcscript.current_lang') + ' `' + allLangs.names[selected_row.lang] + '`\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {}, true );
+				}
+				if ( !( args[1] in allLangs.map ) ) {
+					return msg.replyMsg( lang.get('settings.langinvalid') + '\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n' + lang.get('rcscript.help_lang') + ' `' + Object.values(allLangs.names).join('`, `') + '`', {}, true );
+				}
+
+				msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+					webhook.send( lang.get('rcscript.webhook.updated_lang', allLangs.names[allLangs.map[args[1]]]) ).catch(log_error);
+				}, log_error );
+				return db.run( 'UPDATE rcgcdw SET lang = ? WHERE webhook = ?', [allLangs.map[args[1]], selected_row.webhook], function (error) {
+					if ( error ) {
+						console.log( '- Error while updating the RcGcDw: ' + error );
+						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						return error;
+					}
+					console.log( '- RcGcDw successfully updated.' );
+					msg.replyMsg( lang.get('rcscript.updated_lang') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n`' + cmd + '`', {}, true );
+				} );
+			}
+			if ( args[0] === 'display' ) {
+				if ( !args[1] || !display_types.includes( args[1] ) ) {
+					return msg.replyMsg( lang.get('rcscript.current_display') + ' `' + display_types[selected_row.display] + '`\n`' + cmd + ' display (' + display.join('|') + ')`\n' + display.map( display_type => '`' + display_type + '`: ' + lang.get('rcscript.help_display_' + display_type) ).join('\n'), {}, true );
+				}
+				if ( !display.includes( args[1] ) ) {
+					return msg.replyMsg( lang.get('patreon') + '\n<' + process.env.patreon + '>', {}, true );
+				}
+
+				msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+					webhook.send( lang.get('rcscript.webhook.updated_display_' + args[1]) ).catch(log_error);
+				}, log_error );
+				return db.run( 'UPDATE rcgcdw SET display = ? WHERE webhook = ?', [display_types.indexOf(args[1]), selected_row.webhook], function (error) {
+					if ( error ) {
+						console.log( '- Error while updating the RcGcDw: ' + error );
+						msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+						return error;
+					}
+					console.log( '- RcGcDw successfully updated.' );
+					msg.replyMsg( lang.get('rcscript.updated_display') + ' `' + args[1] + '`\n`' + cmd + '`', {}, true );
+				} );
+			}
+			if ( selected_row.wiki.isFandom() && args[0] === 'feeds' ) {
+				if ( selected_row.wikiid ) {
+					msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+						webhook.send( lang.get('rcscript.webhook.disabled_feeds') ).catch(log_error);
+					}, log_error );
+					return db.run( 'UPDATE rcgcdw SET wikiid = ?, postid = ? WHERE webhook = ?', [null, null, selected_row.webhook], function (error) {
+						if ( error ) {
+							console.log( '- Error while updating the RcGcDw: ' + error );
+							msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+							return error;
+						}
+						console.log( '- RcGcDw successfully updated.' );
+						msg.replyMsg( lang.get('rcscript.disabled_feeds') + '\n`' + cmd + '`', {}, true );
+					} );
+				}
+
+				let scriptPath = selected_row.wiki.replace( /^https:\/\/(.*)\/$/, '$1' );
+				return msg.reactEmoji('⏳', true).then( reaction => got.get( 'https://community.fandom.com/api/v1/Wikis/ByString?includeDomain=true&limit=10&string=' + scriptPath + '&format=json', {
+					responseType: 'json'
+				} ).then( wiresponse => {
+					var wibody = wiresponse.body;
+					if ( wiresponse.statusCode !== 200 || !wibody || wibody.exception || !wibody.items || !wibody.items.length ) {
+						console.log( '- ' + wiresponse.statusCode + ': Error while getting the wiki id: ' + wibody?.exception?.details );
+						if ( reaction ) reaction.removeEmoji();
+						return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+					}
+					var site = wibody.items.find( site => site.domain === scriptPath );
+					if ( site ) return got.get( 'https://services.fandom.com/discussion/' + site.id + '/posts?limit=1&format=json', {
+						headers: {
+							Accept: 'application/hal+json'
+						},
+						responseType: 'json'
+					} ).then( dsresponse => {
+						var dsbody = dsresponse.body;
+						if ( dsresponse.statusCode !== 200 || !dsbody || dsbody.title ) {
+							if ( dsbody?.title !== 'site doesn\'t exists' ) console.log( '- ' + dsresponse.statusCode + ': Error while checking for discussions: ' + dsbody?.title );
+							if ( reaction ) reaction.removeEmoji();
+							return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+						}
+						msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+							webhook.send( lang.get('rcscript.webhook.enabled_feeds', site.name) + '\n<' + selected_row.wiki + 'f>' ).catch(log_error);
+						}, log_error );
+						db.run( 'UPDATE rcgcdw SET wikiid = ?, postid = ? WHERE webhook = ?', [parseInt(site.id, 10), null, selected_row.webhook], function (error) {
+							if ( error ) {
+								console.log( '- Error while updating the RcGcDw: ' + error );
+								if ( reaction ) reaction.removeEmoji();
+								msg.replyMsg( lang.get('settings.save_failed'), {}, true );
+								return error;
+							}
+							console.log( '- RcGcDw successfully updated.' );
+							if ( reaction ) reaction.removeEmoji();
+							msg.replyMsg( lang.get('rcscript.enabled_feeds') + '\n`' + cmd + '`', {}, true );
+						} );
+					}, error => {
+						console.log( '- Error while checking for discussions: ' + error );
+						if ( reaction ) reaction.removeEmoji();
+						return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+					} );
+					console.log( '- No result while getting the wiki id.' );
+					if ( reaction ) reaction.removeEmoji();
+					return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+				}, error => {
+					console.log( '- Error while getting the wiki id: ' + error );
+					if ( reaction ) reaction.removeEmoji();
+					return msg.replyMsg( lang.get('rcscript.no_feeds'), {}, true );
+				} ) );
+			}
+
+			return msg.client.fetchWebhook(...selected_row.webhook.split('/')).then( webhook => {
+				return webhook.channelID;
+			}, error => {
+				log_error(error);
+				if ( error.toString() !== 'DiscordAPIError: Unknown Webhook' ) return;
+				db.run( 'DELETE FROM rcgcdw WHERE webhook = ?', [selected_row.webhook], function (delerror) {
+					if ( delerror ) {
+						console.log( '- Error while removing the RcGcDw: ' + delerror );
+						return delerror;
+					}
+					console.log( '- RcGcDw successfully removed.' );
+				} );
+				Promise.reject();
+			} ).then( channel => {
+				var text = lang.get('rcscript.current_selected') + '\n';
+				text += '\n' + lang.get('rcscript.channel') + ' <#' + channel + '>\n';
+				text += '\n' + lang.get('rcscript.wiki') + ' <' + selected_row.wiki + '>';
+				text += '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n';
+				text += '\n' + lang.get('rcscript.lang') + ' `' + allLangs.names[selected_row.lang] + '`';
+				text += '\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n';
+				text += '\n' + lang.get('rcscript.display') + ' `' + display_types[selected_row.display] + '`';
+				text += '\n`' + cmd + ' display (' + display.join('|') + ')`\n';
+				if ( selected_row.wiki.isFandom() ) {
+					text += '\n' + lang.get('rcscript.feeds') + ' *`' + lang.get('rcscript.' + ( selected_row.wikiid ? 'enabled' : 'disabled' )) + '`*';
+					text += '\n' + lang.get('rcscript.help_feeds') + '\n`' + cmd + ' feeds` ' + lang.get('rcscript.toggle') + '\n';
+				}
+				text += '\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`\n';
+				msg.replyMsg( text, {}, true );
+			}, () => msg.replyMsg( lang.get('rcscript.deleted'), {}, true ) );
+		}
+
+		Promise.all(rows.map( row => msg.client.fetchWebhook(...row.webhook.split('/')).catch( error => {
+			log_error(error);
+			if ( error.toString() !== 'DiscordAPIError: Unknown Webhook' ) return {};
+			db.run( 'DELETE FROM rcgcdw WHERE webhook = ?', [row.webhook], function (delerror) {
+				if ( delerror ) {
+					console.log( '- Error while removing the RcGcDw: ' + delerror );
+					return delerror;
+				}
+				console.log( '- RcGcDw successfully removed.' );
+			} );
+			return;
+		} ) )).then( webhooks => {
+			rows.forEach( (row, i) => {
+				if ( webhooks[i] ) row.channel = webhooks[i].channelID;
+			} );
+			rows = rows.filter( row => row.channel );
+			var only = ( rows.length === 1 );
+			var text = '';
+			if ( rows.length ) text += lang.get('rcscript.current') + rows.map( row => {
+				var cmd = prefix + 'rcscript' + ( only ? '' : ' ' + row.configid );
+				var row_text = '\n';
+				if ( !only ) row_text += '\n`' + cmd + '`';
+				row_text += '\n' + lang.get('rcscript.channel') + ' <#' + row.channel + '>';
+				if ( only ) row_text += '\n';
+				row_text += '\n' + lang.get('rcscript.wiki') + ' <' + row.wiki + '>';
+				if ( only ) row_text += '\n`' + cmd + ' wiki ' + lang.get('rcscript.new_wiki') + '`\n';
+				row_text += '\n' + lang.get('rcscript.lang') + ' `' + allLangs.names[row.lang] + '`';
+				if ( only ) row_text += '\n`' + cmd + ' lang ' + lang.get('rcscript.new_lang') + '`\n';
+				row_text += '\n' + lang.get('rcscript.display') + ' `' + display_types[row.display] + '`';
+				if ( only ) row_text += '\n`' + cmd + ' display (' + display.join('|') + ')`\n';
+				if ( row.wiki.isFandom() ) {
+					row_text += '\n' + lang.get('rcscript.feeds') + ' *`' + lang.get('rcscript.' + ( row.wikiid ? 'enabled' : 'disabled' )) + '`*';
+					if ( only ) row_text += '\n' + lang.get('rcscript.help_feeds') + '\n`' + cmd + ' feeds` ' + lang.get('rcscript.toggle') + '\n';
+				}
+				if ( only ) row_text += '\n' + lang.get('rcscript.delete') + '\n`' + cmd + ' delete`\n';
+				return row_text;
+			} ).join('');
+			else text += lang.get('rcscript.missing');
+			if ( rows.length < limit ) text += '\n\n' + lang.get('rcscript.add_more') + '\n`' + prefix + 'rcscript add ' + lang.get('rcscript.new_wiki') + '`';
+			msg.sendChannel( '<@' + msg.author.id + '>, ' + text, {split:true}, true );
+		} );
+	} );
+}
+
+/**
+ * Turn user input into a wiki.
+ * @param {String} input - The user input referring to a wiki.
+ */
+function input_to_wiki(input) {
+	var regex = input.match( /^(?:https:\/\/)?([a-z\d-]{1,50}\.(?:gamepedia\.com|(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/[a-z-]{2,12})?))(?:\/|$)/ );
+	if ( regex ) return 'https://' + regex[1] + '/';
+	if ( input.startsWith( 'https://' ) ) {
+		let project = wikiProjects.find( project => input.split('/')[2].endsWith( project.name ) );
+		if ( project ) {
+			regex = input.match( new RegExp( project.regex + `(?:${project.articlePath}|${project.scriptPath}|/?$)` ) );
+			if ( regex ) return 'https://' + regex[1] + project.scriptPath;
+		}
+		let wiki = input.replace( /\/(?:api|index)\.php(?:|\?.*)$/, '/' );
+		if ( !wiki.endsWith( '/' ) ) wiki += '/';
+		return wiki;
+	}
+	let project = wikiProjects.find( project => input.split('/')[0].endsWith( project.name ) );
+	if ( project ) {
+		regex = input.match( new RegExp( project.regex + `(?:${project.articlePath}|${project.scriptPath}|/?$)` ) );
+		if ( regex ) return 'https://' + regex[1] + project.scriptPath;
+	}
+	if ( allSites.some( site => site.wiki_domain === input + '.gamepedia.com' ) ) {
+		return 'https://' + input + '.gamepedia.com/';
+	}
+	if ( /^(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(input) ) {
+		if ( !input.includes( '.' ) ) return 'https://' + input + '.fandom.com/';
+		else return 'https://' + input.split('.')[1] + '.fandom.com/' + input.split('.')[0] + '/';
+	}
+	return;
+}
+
+module.exports = {
+	name: 'rcscript',
+	everyone: rcscriptExists,
+	pause: rcscriptExists,
+	owner: false,
+	run: cmd_rcscript
+};

+ 3 - 0
functions/special_page.js

@@ -126,6 +126,9 @@ function special_page(lang, msg, title, specialpage, embed, wiki, reaction, spoi
 		overwrites[specialpage](this, lang, msg, wiki, reaction, spoiler, args, embed);
 		return;
 	}
+	if ( specialpage === 'recentchanges' && msg.isAdmin() && msg.guild.id in patreon ) {
+		embed.addField( lang.get('rcscript.title'), lang.get('rcscript.ad', ( patreons[msg?.guild?.id] || process.env.prefix )) );
+	}
 	got.get( wiki + 'api.php?action=query&meta=siteinfo|allmessages&siprop=general&amenableparser=true&amtitle=' + encodeURIComponent( title ) + '&ammessages=' + ( specialpage in descriptions ? descriptions[specialpage] : encodeURIComponent( specialpage ) + '-summary' ) + ( specialpage in querypages ? querypages[specialpage][0] : '' ) + '&format=json', {
 		responseType: 'json'
 	} ).then( response => {

+ 18 - 0
i18n/allLangs.json

@@ -40,5 +40,23 @@
 			"chinese": "zh",
 			"中文": "zh"
 		}
+	},
+	"RcGcDw": {
+		"names": {
+			"en": "English",
+			"de": "German",
+			"pl": "Polish"
+		},
+		"map": {
+			"en": "en",
+			"eng": "en",
+			"english": "en",
+			"de": "de",
+			"german": "de",
+			"deutsch": "de",
+			"pl": "pl",
+			"polish": "pl",
+			"polski": "pl"
+		}
 	}
 }

+ 55 - 0
i18n/en.json

@@ -151,6 +151,61 @@
 		"help_fandom": "https://community.fandom.com/wiki/Special:VerifyUser",
 		"help_subpage": "Please add your Discord tag ($1) to your Discord subpage on the wiki:"
 	},
+	"rcscript": {
+		"title": "Recent changes webhook",
+		"ad": "You want recent changes directly in Discord? Use `$1rcscript` to add a **Recent changes Goat compatible Discord webhook** to your Discord server!",
+		"missing": "there are no recent changes webhooks for this server yet.",
+		"current": "these are the current recent changes webhooks for this server:",
+		"current_selected": "this is the recent changes webhook for this server:",
+		"channel": "Channel:",
+		"wiki": "Wiki:",
+		"lang": "Language:",
+		"display": "Display mode:",
+		"feeds": "Feeds based changes:",
+		"enabled": "enabled",
+		"disabled": "disabled",
+		"toggle": "(toggle)",
+		"help_feeds": "(discussions, message walls, article comments)",
+		"delete": "Delete this recent changes webhook:",
+		"add_more": "Add more recent changes webhooks:",
+		"noadmin": "you need the `Manage Webhooks` permission for this command!",
+		"max_entries":"you already reached the maximal amount of recent changes webhooks.",
+		"sysmessage": "the system message `$1` has to be the server ID `$2` to add a recent changes webhook.",
+		"audit_reason": "Recent changes webhook for \"$1\"",
+		"audit_reason_delete": "Removed recent changes webhook",
+		"added": "a recent changes webhook has been added for:",
+		"deleted":"the recent changes webhook has be deleted.",
+		"webhook_failed": "sadly the webhook couldn't be created, please try again later.",
+		"current_wiki": "the wiki for this webhook is:",
+		"help_wiki": "Link to the wiki: `https://<wiki>.gamepedia.com/` or `https://<wiki>.fandom.com/`",
+		"new_wiki": "<new wiki>",
+		"updated_wiki":"the wiki for this webhook has been changed to:",
+		"current_lang": "the language for this webhook is:",
+		"help_lang": "Currently supported languages are:",
+		"new_lang": "<new language>",
+		"updated_lang": "the language for this webhook has been changed to:",
+		"current_display": "the display mode for this webhook is:",
+		"help_display_compact": "Compact text messages with inline links.",
+		"help_display_embed": "Embed messages with edit tags and category changes.",
+		"help_display_image": "Embed messages with image previews.",
+		"help_display_diff": "Embed messages with image previews and edit differences.",
+		"updated_display": "the display mode for this webhook has been changed to:",
+		"disabled_feeds": "the feeds based changes, like discussions, message walls and article comments, for this webhook have been disabled.",
+		"enabled_feeds": "the feeds based changes, like discussions, message walls and article comments, for this webhook have been enabled.",
+		"no_feeds": "the wiki for this webhook has no feeds based features, like discussions, message walls and article comments, enabled.",
+		"webhook": {
+			"created": "A recent changes webhook for $1 has been added to this channel.",
+			"updated_wiki": "The wiki has been changed to $1 for this recent changes webhook.",
+			"updated_lang": "The language has been changed to `$1` for this recent changes webhook.",
+			"updated_display_compact": "The display mode has been changed to compact text messages with inline links for this recent changes webhook.",
+			"updated_display_embed": "The display mode has been changed to embed messages with edit tags and category changes for this recent changes webhook.",
+			"updated_display_image": "The display mode has been changed to embed messages with image previews for this recent changes webhook.",
+			"updated_display_diff": "The display mode has been changed to embed messages with image previews and edit differences for this recent changes webhook.",
+			"disabled_feeds": "The feeds based changes, like discussions, message walls and article comments, have been disabled for this recent changes webhook.",
+			"enabled_feeds": "The feeds based changes, like discussions, message walls and article comments, have been enabled for this recent changes webhook.",
+			"deleted": "This recent changes webhook will be deleted."
+		}
+	},
 	"overview": {
 		"inaccurate": "Statistics may be inaccurate",
 		"name": "Full name:",

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "discord-wiki-bot",
-  "version": "2.2.0",
+  "version": "2.3.0",
   "description": "Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about the pages and is able to resolve redirects and follow interwiki links.",
   "main": "main.js",
   "scripts": {

+ 31 - 3
util/database.js

@@ -1,4 +1,4 @@
-const {defaultSettings} = require('../util/default.json');
+const {limit: {rcgcdw: rcgcdwLimit}, defaultSettings} = require('../util/default.json');
 const sqlite3 = require('sqlite3').verbose();
 var db = new sqlite3.Database( './wikibot.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, dberror => {
 	if ( dberror ) {
@@ -42,7 +42,7 @@ function getSettings(trysettings = 1) {
 						console.log( '- ' + shardId + ': Created the patreons index.' );
 					} );
 				} );
-				db.run( 'CREATE TABLE IF NOT EXISTS discord(guild TEXT NOT NULL, channel TEXT, lang TEXT NOT NULL DEFAULT [' + defaultSettings.lang + '], wiki TEXT NOT NULL DEFAULT [' + defaultSettings.wiki + '], prefix TEXT NOT NULL DEFAULT [' + process.env.prefix + '], patreon TEXT, voice INTEGER, inline INTEGER, UNIQUE(guild, channel), FOREIGN KEY(patreon) REFERENCES patreons(patreon) ON DELETE SET NULL)', [], function (error) {
+				db.run( 'CREATE TABLE IF NOT EXISTS discord(guild TEXT NOT NULL, channel TEXT, lang TEXT NOT NULL DEFAULT ?, wiki TEXT NOT NULL DEFAULT ?, prefix TEXT NOT NULL DEFAULT ?, patreon TEXT, voice INTEGER, inline INTEGER, UNIQUE(guild, channel), FOREIGN KEY(patreon) REFERENCES patreons(patreon) ON DELETE SET NULL)', [defaultSettings.lang, defaultSettings.wiki, process.env.prefix], function (error) {
 					if ( error ) {
 						console.log( '- ' + shardId + ': Error while creating the discord table: ' + error );
 						return error;
@@ -88,7 +88,7 @@ function getSettings(trysettings = 1) {
 						getSettings(trysettings);
 					}
 				} );
-				db.run( 'CREATE TABLE IF NOT EXISTS verification(guild TEXT NOT NULL, configid INTEGER NOT NULL, channel TEXT NOT NULL, role TEXT NOT NULL, editcount INTEGER NOT NULL DEFAULT [0], usergroup TEXT NOT NULL DEFAULT [user], accountage INTEGER NOT NULL DEFAULT [0], rename INTEGER NOT NULL DEFAULT [0], UNIQUE(guild, configid))', [], function (error) {
+				db.run( 'CREATE TABLE IF NOT EXISTS verification(guild TEXT NOT NULL, configid INTEGER NOT NULL, channel TEXT NOT NULL, role TEXT NOT NULL, editcount INTEGER NOT NULL DEFAULT ?, usergroup TEXT NOT NULL DEFAULT ?, accountage INTEGER NOT NULL DEFAULT ?, rename INTEGER NOT NULL DEFAULT ?, UNIQUE(guild, configid))', [0, 'user', 0, 0], function (error) {
 					if ( error ) {
 						console.log( '- ' + shardId + ': Error while creating the verification table: ' + error );
 						return error;
@@ -102,6 +102,34 @@ function getSettings(trysettings = 1) {
 						console.log( '- ' + shardId + ': Created the verification index.' );
 					} );
 				} );
+				db.run( 'CREATE TABLE IF NOT EXISTS rcgcdw(guild TEXT NOT NULL, configid INTEGER NOT NULL, webhook TEXT NOT NULL UNIQUE, wiki TEXT NOT NULL, lang TEXT NOT NULL DEFAULT ?, display INTEGER NOT NULL DEFAULT ?, wikiid INTEGER, rcid INTEGER, postid TEXT, UNIQUE(guild, configid))', [defaultSettings.lang, rcgcdwLimit.display], function (error) {
+					if ( error ) {
+						console.log( '- ' + shardId + ': Error while creating the rcgcdw table: ' + error );
+						return error;
+					}
+					console.log( '- ' + shardId + ': Created the rcgcdw table.' );
+					db.run( 'CREATE INDEX idx_rcgcdw_wiki ON rcgcdw(wiki)', [], function (idxerror) {
+						if ( idxerror ) {
+							console.log( '- ' + shardId + ': Error while creating the rcgcdw wiki index: ' + idxerror );
+							return idxerror;
+						}
+						console.log( '- ' + shardId + ': Created the rcgcdw wiki index.' );
+					} );
+					db.run( 'CREATE INDEX idx_rcgcdw_webhook ON rcgcdw(webhook)', [], function (idxerror) {
+						if ( idxerror ) {
+							console.log( '- ' + shardId + ': Error while creating the rcgcdw webhook index: ' + idxerror );
+							return idxerror;
+						}
+						console.log( '- ' + shardId + ': Created the rcgcdw webhook index.' );
+					} );
+					db.run( 'CREATE INDEX idx_rcgcdw_config ON rcgcdw(guild, configid ASC)', [], function (idxerror) {
+						if ( idxerror ) {
+							console.log( '- ' + shardId + ': Error while creating the rcgcdw config index: ' + idxerror );
+							return idxerror;
+						}
+						console.log( '- ' + shardId + ': Created the rcgcdw config index.' );
+					} );
+				} );
 			} );
 			else {
 				if ( trysettings < 10 ) {

+ 6 - 1
util/default.json

@@ -1,5 +1,4 @@
 {
-	"defaultPermissions": 403033152,
 	"limit": {
 		"command": {
 			"default": 10,
@@ -16,8 +15,14 @@
 		"verification": {
 			"default": 10,
 			"patreon": 15
+		},
+		"rcgcdw": {
+			"default": 1,
+			"patreon": 1,
+			"display": 1
 		}
 	},
+	"defaultPermissions": 939912256,
 	"defaultSettings": {
 		"lang": "en",
 		"wiki": "https://community.fandom.com/"