Browse Source

Switch to PostgreSQL

Markus-Rost 4 years ago
parent
commit
5747fdd610
13 changed files with 740 additions and 527 deletions
  1. 8 1
      .env.example
  2. 41 48
      bot.js
  3. 114 115
      cmds/eval.js
  4. 11 13
      cmds/get.js
  5. 7 9
      cmds/verify.js
  6. 7 11
      cmds/voice.js
  7. 3 6
      dashboard/index.js
  8. 12 20
      dashboard/settings.js
  9. 4 13
      dashboard/util.js
  10. 66 186
      database.js
  11. 449 38
      package-lock.json
  12. 1 0
      package.json
  13. 17 67
      util/database.js

+ 8 - 1
.env.example

@@ -25,4 +25,11 @@ phabricator-wikimedia=""
 # Optional: API token for phabricator.miraheze.org
 phabricator-miraheze=""
 # Optional: Path to a log file for usage statistics
-usagelog=""
+usagelog=""
+
+# Variables for your PostgreSQL server
+PGHOST="localhost"
+PGUSER="postgres"
+PGDATABASE="postgres"
+PGPASSWORD=""
+PGPORT="5432"

+ 41 - 48
bot.js

@@ -237,26 +237,7 @@ client.on( 'raw', rawEvent => {
 	if ( !interaction.guild_id ) {
 		return slash[interaction.data.name](interaction, new Lang(), new Wiki(), channel);
 	}
-	db.get( 'SELECT wiki, lang, role FROM discord WHERE guild = ? AND (channel = ? OR channel = ? OR channel IS NULL) ORDER BY channel DESC', [interaction.guild_id, interaction.channel_id, '#' + channel?.parentID], (dberror, row) => {
-		if ( dberror ) {
-			console.log( '- Error while getting the wiki: ' + dberror );
-			return got.post( `https://discord.com/api/v8/interactions/${interaction.id}/${interaction.token}/callback`, {
-				json: {
-					type: 4,
-					data: {
-						content: '[<:error:440871715938238494> Error! <:error:440871715938238494>](<' + process.env.invite + '>)',
-						allowed_mentions: {
-							parse: []
-						},
-						flags: 64
-					}
-				}
-			} ).then( response => {
-				if ( response.statusCode !== 204 ) {
-					console.log( '- Slash: ' + response.statusCode + ': Error while sending the response: ' + response.body?.message );
-				}
-			}, log_error );
-		}
+	db.query( 'SELECT wiki, lang, role FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', [interaction.guild_id, interaction.channel_id, '#' + channel?.parentID] ).then( ({rows:[row]}) => {
 		var lang = new Lang(( row?.lang || channel?.guild?.preferredLocale ));
 		if ( row?.role && !interaction.member.roles.includes( row.role ) && channel?.guild?.roles.cache.has(row.role) && ( !interaction.member.roles.length || !interaction.member.roles.some( role => channel.guild.roles.cache.get(role)?.comparePositionTo(row.role) >= 0 ) ) ) {
 			return got.post( `https://discord.com/api/v8/interactions/${interaction.id}/${interaction.token}/callback`, {
@@ -278,6 +259,24 @@ client.on( 'raw', rawEvent => {
 		}
 		var wiki = new Wiki(row?.wiki);
 		return slash[interaction.data.name](interaction, lang, wiki, channel);
+	}, dberror => {
+		console.log( '- Slash: Error while getting the wiki: ' + dberror );
+		return got.post( `https://discord.com/api/v8/interactions/${interaction.id}/${interaction.token}/callback`, {
+			json: {
+				type: 4,
+				data: {
+					content: '[<:error:440871715938238494> Error! <:error:440871715938238494>](<' + process.env.invite + '>)',
+					allowed_mentions: {
+						parse: []
+					},
+					flags: 64
+				}
+			}
+		} ).then( response => {
+			if ( response.statusCode !== 204 ) {
+				console.log( '- Slash: ' + response.statusCode + ': Error while sending the response: ' + response.body?.message );
+			}
+		}, log_error );
 	} );
 } );
 
@@ -287,11 +286,14 @@ client.on( 'message', msg => {
 		if ( msg.content === process.env.prefix + 'help' && ( msg.isAdmin() || msg.isOwner() ) ) {
 			if ( msg.channel.permissionsFor(msg.client.user).has('SEND_MESSAGES') ) {
 				console.log( msg.guild.name + ': ' + msg.content );
-				db.get( 'SELECT lang FROM discord WHERE guild = ? AND (channel = ? OR channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID], (dberror, row) => {
-					if ( dberror ) console.log( '- Error while getting the lang: ' + dberror );
+				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', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID] ).then( ({rows:[row]}) => {
 					msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('prefix', patreons[msg.guild.id]), {}, true );
+				}, dberror => {
+					console.log( '- Error while getting the lang: ' + dberror );
+					msg.replyMsg( new Lang(msg.guild.preferredLocale, 'general').get('prefix', patreons[msg.guild.id]), {}, true );
 				} );
 			}
+			return;
 		}
 		if ( !( msg.content.includes( '[[' ) && msg.content.includes( ']]' ) ) && !( msg.content.includes( '{{' ) && msg.content.includes( '}}' ) ) ) return;
 	}
@@ -299,27 +301,20 @@ client.on( 'message', msg => {
 		var permissions = msg.channel.permissionsFor(msg.client.user);
 		var missing = permissions.missing(['SEND_MESSAGES','ADD_REACTIONS','USE_EXTERNAL_EMOJIS','READ_MESSAGE_HISTORY']);
 		if ( missing.length ) {
-			if ( msg.isAdmin() || msg.isOwner() ) {
+			if ( ( msg.isAdmin() || msg.isOwner() ) && msg.content.hasPrefix(( patreons[msg.guild.id] || process.env.prefix ), 'm') ) {
 				console.log( msg.guild.id + ': Missing permissions - ' + missing.join(', ') );
 				if ( !missing.includes( 'SEND_MESSAGES' ) ) {
-					db.get( 'SELECT lang FROM discord WHERE guild = ? AND (channel = ? OR channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID], (dberror, row) => {
-						if ( dberror ) console.log( '- Error while getting the lang: ' + dberror );
-						if ( msg.content.hasPrefix(( patreons[msg.guild.id] || process.env.prefix ), 'm') ) {
-							msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('missingperm') + ' `' + missing.join('`, `') + '`', {}, true );
-						}
+					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', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID] ).then( ({rows:[row]}) => {
+						msg.replyMsg( new Lang(( row?.lang || msg.guild.preferredLocale ), 'general').get('missingperm') + ' `' + missing.join('`, `') + '`', {}, true );
+					}, dberror => {
+						console.log( '- Error while getting the lang: ' + dberror );
+						msg.replyMsg( new Lang(msg.guild.preferredLocale, 'general').get('missingperm') + ' `' + missing.join('`, `') + '`', {}, true );
 					} );
 				}
 			}
 			return;
 		}
-		db.get( 'SELECT wiki, lang, role, inline FROM discord WHERE guild = ? AND (channel = ? OR channel = ? OR channel IS NULL) ORDER BY channel DESC', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID], (dberror, row) => {
-			if ( dberror ) {
-				console.log( '- Error while getting the wiki: ' + dberror );
-				if ( permissions.has('SEND_MESSAGES') ) {
-					msg.sendChannel( new Lang(msg.guild.preferredLocale, 'general').get('database') + '\n' + process.env.invite, {}, true );
-				}
-				return dberror;
-			}
+		db.query( 'SELECT wiki, lang, role, inline FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', [msg.guild.id, msg.channel.id, '#' + msg.channel.parentID] ).then( ({rows:[row]}) => {
 			if ( row ) {
 				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;
@@ -330,6 +325,9 @@ client.on( 'message', msg => {
 				msg.defaultSettings = true;
 				newMessage(msg, new Lang(msg.guild.preferredLocale));
 			}
+		}, dberror => {
+			console.log( '- Error while getting the wiki: ' + dberror );
+			msg.sendChannel( new Lang(msg.guild.preferredLocale, 'general').get('database') + '\n' + process.env.invite, {}, true );
 		} );
 	}
 	else newMessage(msg, new Lang());
@@ -378,14 +376,12 @@ client.on( 'guildDelete', guild => {
 function removeSettings(guild) {
 	leftGuilds.delete(guild);
 	if ( client.guilds.cache.has(guild) ) return;
-	db.run( 'DELETE FROM discord WHERE main = ?', [guild], function (dberror) {
-		if ( dberror ) {
-			console.log( '- ' + guild + ': Error while removing the settings: ' + dberror );
-			return dberror;
-		}
+	db.query( 'DELETE FROM discord WHERE main = $1', [guild] ).then( ({rowCount}) => {
 		if ( patreons.hasOwnProperty(guild) ) client.shard.broadcastEval( `delete global.patreons['${guild}']` );
 		if ( voice.hasOwnProperty(guild) ) delete voice[guild];
-		if ( this.changes ) console.log( '- ' + guild + ': Settings successfully removed.' );
+		if ( rowCount ) console.log( '- ' + guild + ': Settings successfully removed.' );
+	}, dberror => {
+		console.log( '- ' + guild + ': Error while removing the settings: ' + dberror );
 	} );
 }
 
@@ -456,13 +452,10 @@ function graceful(signal) {
 	setTimeout( () => {
 		console.log( '- ' + shardId + ': ' + signal + ': Destroying client...' );
 		client.destroy();
-		db.close( dberror => {
-			if ( dberror ) {
-				console.log( '- ' + shardId + ': ' + signal + ': Error while closing the database connection: ' + dberror );
-				return dberror;
-			}
+		db.end().then( () => {
 			console.log( '- ' + shardId + ': ' + signal + ': Closed the database connection.' );
-			process.exit(0);
+		}, dberror => {
+			console.log( '- ' + shardId + ': ' + signal + ': Error while closing the database connection: ' + dberror );
 		} );
 	}, 1000 ).unref();
 }

+ 114 - 115
cmds/eval.js

@@ -45,11 +45,8 @@ async function cmd_eval(lang, msg, args, line, wiki) {
  * @param {String[]} [sqlargs] - The command arguments.
  */
 function database(sql, sqlargs = []) {
-	return new Promise( function (resolve, reject) {
-		db.all( sql, sqlargs, function(error, rows) {
-			if (error) reject.call(this, error);
-			else resolve.call(this, rows);
-		} );
+	return db.query( sql, sqlargs ).then( ({rows}) => {
+		return rows;
 	} );
 }
 
@@ -109,10 +106,10 @@ function checkWiki(wiki) {
 			result.activity.push(`${rc.length} edits in${text}`);
 		}
 		return Promise.all([
-			database('SELECT guild, lang, display, rcid, postid FROM rcgcdw WHERE wiki = ?', [result.wiki]).then( rows => {
+			db.query( 'SELECT guild, lang, display, rcid, postid FROM rcgcdw WHERE wiki = $1', [result.wiki] ).then( ({rows}) => {
 				result.rcgcdb = rows;
-			}, error => {
-				result.rcgcdb = error.toString();
+			}, dberror => {
+				result.rcgcdb = dberror.toString();
 			} ),
 			( wiki.isFandom() ? got.get( wiki + 'wikia.php?controller=DiscussionPost&method=getPosts&includeCounters=false&sortDirection=descending&sortKey=creation_date&limit=100&format=json&cache=' + Date.now(), {
 				headers: {
@@ -169,91 +166,96 @@ function checkWiki(wiki) {
  * @param {Discord.Message} msg - The Discord message.
  */
 function removePatreons(guild, msg) {
-	try {
-		if ( !( typeof guild === 'string' || msg instanceof Discord.Message ) ) {
-			return 'removePatreons(guild, msg) – No guild or message provided!';
-		}
-		db.get( 'SELECT lang, inline FROM discord WHERE guild = ? AND channel IS NULL', [guild], (dberror, row) => {
-			try {
-				if ( dberror ) {
-					console.log( '- Error while getting the guild: ' + dberror );
-					msg.replyMsg( 'I got an error while searching for the guild!', {}, true );
-					return dberror;
+	if ( !( typeof guild === 'string' || msg instanceof Discord.Message ) ) {
+		return 'removePatreons(guild, msg) – No guild or message provided!';
+	}
+	var messages = [];
+	db.connect().then( client => {
+		client.query( 'SELECT lang, inline FROM discord WHERE guild = $1 AND channel IS NULL', [guild] ).then( ({rows:[row]}) => {
+			if ( !row ) {
+				messages.push('The guild doesn\'t exist!');
+				return Promise.reject();
+			}
+			return client.query( 'UPDATE discord SET lang = $1, inline = $2, prefix = $3, patreon = NULL WHERE guild = $4', [row.lang, row.inline, process.env.prefix, guild] ).then( ({rowCount}) => {
+				if ( rowCount ) {
+					console.log( '- Guild successfully updated.' );
+					messages.push('Guild successfully updated.');
 				}
-				if ( !row ) {
-					msg.replyMsg( 'that guild doesn\'t exist!', {}, true );
-					return;
+				msg.client.shard.broadcastEval( `delete global.patreons['${guild}']`);
+			}, dberror => {
+				console.log( '- Error while updating the guild: ' + dberror );
+				messages.push('Error while updating the guild: ' + dberror);
+				return Promise.reject();
+			} );
+		}, dberror => {
+			console.log( '- Error while getting the guild: ' + dberror );
+			messages.push('Error while getting the guild: ' + dberror);
+			return Promise.reject();
+		} ).then( () => {
+			return client.query( 'DELETE FROM discord WHERE guild = $1 AND channel LIKE $2', [guild, '#%'] ).then( ({rowCount}) => {
+				if ( rowCount ) {
+					console.log( '- Channel categories successfully deleted.' );
+					messages.push('Channel categories successfully deleted.');
 				}
-				db.run( 'UPDATE discord SET lang = ?, inline = ?, prefix = ?, patreon = NULL WHERE guild = ?', [row.lang, row.inline, process.env.prefix, guild], function (error) {
-					try {
-						if ( error ) {
-							console.log( '- Error while updating the guild: ' + error );
-							msg.replyMsg( 'I got an error while updating the guild!', {}, true );
-							return error;
-						}
-						console.log( '- Guild successfully updated.' );
-						msg.client.shard.broadcastEval( `delete global.patreons['${guild}']`);
-						msg.replyMsg( 'the patreon features are now disabled on that guild.', {}, true );
-					}
-					catch ( tryerror ) {
-						console.log( '- Error while removing the patreon features: ' + tryerror );
-					}
-				} );
-			}
-			catch ( tryerror ) {
-				console.log( '- Error while removing the patreon features: ' + tryerror );
-			}
-		} );
-		db.run( 'DELETE FROM discord WHERE guild = ? AND channel LIKE ?', [guild, '#%'], function (dberror) {
-			if ( dberror ) {
+			}, dberror => {
 				console.log( '- Error while deleting the channel categories: ' + dberror );
-				return dberror;
-			}
-			if ( this.changes ) console.log( '- Channel categories successfully deleted.' );
-		} );
-		db.all( 'SELECT configid FROM verification WHERE guild = ? ORDER BY configid ASC', [guild], (dberror, rows) => {
-			if ( dberror ) {
-				console.log( '- Error while getting the verifications: ' + dberror );
-				return dberror;
-			}
-			var ids = rows.slice(verificationLimit.default).map( row => row.configid );
-			if ( ids.length ) db.run( 'DELETE FROM verification WHERE guild = ? AND configid IN (' + ids.map( configid => '?' ).join(', ') + ')', [guild, ...ids], function (error) {
-				if ( error ) {
-					console.log( '- Error while deleting the verifications: ' + error );
-					return error;
+				messages.push('Error while deleting the channel categories: ' + dberror);
+			} );
+		} ).then( () => {
+			return client.query( 'SELECT configid FROM verification WHERE guild = $1 ORDER BY configid ASC OFFSET $2', [guild, verificationLimit.default] ).then( ({rows}) => {
+				if ( rows.length ) {
+					return client.query( 'DELETE FROM verification WHERE guild = $1 AND configid IN (' + rows.map( (row, i) => '$' + ( i + 2 ) ).join(', ') + ')', [guild, ...rows.map( row => row.configid )] ).then( () => {
+						console.log( '- Verifications successfully deleted.' );
+						messages.push('Verifications successfully deleted.');
+					}, dberror => {
+						console.log( '- Error while deleting the verifications: ' + dberror );
+						messages.push('Error while deleting the verifications: ' + dberror);
+					} );
 				}
-				console.log( '- Verifications successfully deleted.' );
+			}, dberror => {
+				console.log( '- Error while getting the verifications: ' + dberror );
+				messages.push('Error while getting the verifications: ' + dberror);
 			} );
-		} );
-		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;
+		} ).then( () => {
+			return client.query( 'SELECT webhook FROM rcgcdw WHERE guild = $1 ORDER BY configid ASC OFFSET $2', [guild, rcgcdwLimit.default] ).then( ({rows}) => {
+				if ( rows.length ) {
+					return client.query( 'DELETE FROM rcgcdw WHERE webhook IN (' + rows.map( (row, i) => '$' + ( i + 1 ) ).join(', ') + ')', rows.map( row => row.webhook ) ).then( () => {
+						console.log( '- RcGcDw successfully deleted.' );
+						messages.push('RcGcDw successfully deleted.');
+						rows.forEach( row => msg.client.fetchWebhook(...row.webhook.split('/')).then( webhook => {
+							webhook.delete('Removed extra recent changes webhook').catch(log_error);
+						}, log_error ) );
+					}, dberror => {
+						console.log( '- Error while deleting the RcGcDw: ' + dberror );
+						messages.push('Error while deleting the RcGcDw: ' + dberror);
+					} );
 				}
-				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 ) );
+			}, dberror => {
+				console.log( '- Error while getting the RcGcDw: ' + dberror );
+				messages.push('Error while getting the RcGcDw: ' + dberror);
 			} );
-		} );
-		db.run( 'UPDATE rcgcdw SET display = ? WHERE guild = ? AND display > ?', [rcgcdwLimit.display, guild, rcgcdwLimit.display], function (dberror) {
-			if ( dberror ) {
+		} ).then( () => {
+			return client.query( 'UPDATE rcgcdw SET display = $1 WHERE guild = $2 AND display > $1', [rcgcdwLimit.display, guild] ).then( () => {
+				console.log( '- RcGcDw successfully updated.' );
+				messages.push('RcGcDw successfully updated.');
+			}, dberror => {
 				console.log( '- Error while updating the RcGcDw: ' + dberror );
-				return dberror;
+				messages.push('Error while updating the RcGcDw: ' + dberror);
+			} );
+		} ).then( () => {
+			return messages;
+		}, error => {
+			if ( error ) {
+				console.log( '- Error while removing the patreon features: ' + error );
+				messages.push('Error while removing the patreon features: ' + error);
 			}
-			console.log( '- RcGcDw successfully updated.' );
+			return messages;
+		} ).finally( () => {
+			client.release();
 		} );
-	}
-	catch ( tryerror ) {
-		console.log( '- Error while removing the patreon features: ' + tryerror );
-		return 'removePatreons(guild, msg) – Error while removing the patreon features: ' + tryerror;
-	}
+	}, dberror => {
+		console.log( '- Error while connecting to the database client: ' + dberror );
+	} );
 }
 
 /**
@@ -261,7 +263,7 @@ function removePatreons(guild, msg) {
  * @param {Discord.Message} msg - The Discord message.
  */
 function removeSettings(msg) {
-	if ( !msg ) return 'removeSettings(msg) – No message provided!';
+	if ( !( msg instanceof Discord.Message ) ) return 'removeSettings(msg) – No message provided!';
 	try {
 		msg.client.shard.broadcastEval( `[
 			[...this.guilds.cache.keys()],
@@ -273,44 +275,41 @@ function removeSettings(msg) {
 			var all_channels = results.map( result => result[1] ).reduce( (acc, val) => acc.concat(val), [] );
 			var guilds = [];
 			var channels = [];
-			db.each( 'SELECT guild, channel FROM discord', [], (dberror, row) => {
-				if ( dberror ) {
-					console.log( '- Error while getting the setting: ' + dberror );
-					return dberror;
-				}
-				if ( !row.channel && !all_guilds.includes(row.guild) ) {
-					if ( patreons.hasOwnProperty(row.guild) || voice.hasOwnProperty(row.guild) ) {
-						msg.client.shard.broadcastEval( `delete global.patreons['${row.guild}'];
-						delete global.voice['${row.guild}'];` );
+			db.query( 'SELECT guild, channel FROM discord' ).then( ({rows}) => {
+				rows.forEach( row => {
+					if ( !all_guilds.includes(row.guild) ) {
+						if ( !row.channel ) {
+							if ( patreons.hasOwnProperty(row.guild) || voice.hasOwnProperty(row.guild) ) {
+								msg.client.shard.broadcastEval( `delete global.patreons['${row.guild}'];
+								delete global.voice['${row.guild}'];` );
+							}
+							return guilds.push(row.guild);
+						}
 					}
-					return guilds.push(row.guild);
-				}
-				if ( row.channel && all_guilds.includes(row.guild) && !all_channels.includes(row.channel) ) return channels.push(row.channel);
-			}, (error) => {
-				if ( error ) {
-					console.log( '- Error while getting the settings: ' + error );
-					msg.replyMsg( 'I got an error while getting the settings!', {}, true );
-					return error;
-				}
+					else if ( row.channel && !all_channels.includes(row.channel) ) {
+						return channels.push(row.channel);
+					}
+				} );
 				if ( guilds.length ) {
-					db.run( 'DELETE FROM discord WHERE main IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
-						if ( dberror ) {
-							console.log( '- Error while removing the guilds: ' + dberror );
-							msg.replyMsg( 'I got an error while removing the guilds!', {}, true );
-							return dberror;
-						}
-						console.log( '- Guilds successfully removed: ' + this.changes );
+					db.query( 'DELETE FROM discord WHERE main IN (' + guilds.map( (guild, i) => '$' + ( i + 1 ) ).join(', ') + ')', guilds ).then( ({rowCount}) => {
+						console.log( '- Guilds successfully removed: ' + rowCount );
+					}, dberror => {
+						console.log( '- Error while removing the guilds: ' + dberror );
+						msg.replyMsg( 'I got an error while removing the guilds!', {}, true );
 					} );
 				}
-				if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( channel => '?' ).join(', ') + ')', channels, function (dberror) {
-					if ( dberror ) {
+				if ( channels.length ) {
+					db.query( 'DELETE FROM discord WHERE channel IN (' + channels.map( (channel, i) => '$' + ( i + 1 ) ).join(', ') + ')', channels ).then( ({rowCount}) => {
+						console.log( '- Channels successfully removed: ' + rowCount );
+					}, dberror => {
 						console.log( '- Error while removing the channels: ' + dberror );
 						msg.replyMsg( 'I got an error while removing the channels!', {}, true );
-						return dberror;
-					}
-					console.log( '- Channels successfully removed: ' + this.changes );
-				} );
+					} );
+				}
 				if ( !guilds.length && !channels.length ) console.log( '- Settings successfully removed.' );
+			}, dberror => {
+				console.log( '- Error while getting the settings: ' + dberror );
+				msg.replyMsg( 'I got an error while getting the settings!', {}, true );
 			} );
 		} );
 	}

+ 11 - 13
cmds/get.js

@@ -34,18 +34,17 @@ async function cmd_get(lang, msg, args, line, wiki) {
 			var guildchannel = ['Updates channel:', '`' + guild.channel + '`'];
 			var guildsettings = ['Settings:', '*unknown*'];
 			
-			return db.all( 'SELECT channel, wiki, lang, role, inline, prefix FROM discord WHERE guild = ? ORDER BY channel ASC', [guild.id], (dberror, rows) => {
-				if ( dberror ) {
-					console.log( '- Error while getting the settings: ' + dberror );
-				}
-				else if ( rows.length ) {
-					row = rows.find( row => !row.channel );
+			return db.query( 'SELECT channel, wiki, lang, role, inline, prefix FROM discord WHERE guild = $1 ORDER BY channel ASC', [guild.id] ).then( ({rows}) => {
+				if ( rows.length ) {
+					let row = rows.find( row => !row.channel );
 					row.patreon = patreons.hasOwnProperty(guild.id);
 					row.voice = guild.voice;
 					guildsettings[1] = '```json\n' + JSON.stringify( rows, null, '\t' ) + '\n```';
 				}
 				else guildsettings[1] = '*default*';
-				
+			}, dberror => {
+				console.log( '- Error while getting the settings: ' + dberror );
+			} ).then( () => {
 				if ( msg.showEmbed() ) {
 					var embed = new MessageEmbed().setThumbnail( guild.icon ).addField( guildname[0], guildname[1] ).addField( guildowner[0], guildowner[1] ).addField( guildsize[0], guildsize[1], true ).addField( guildshard[0], guildshard[1], true ).addField( guildpermissions[0], guildpermissions[1] );
 					if ( guild.channel ) embed.addField( guildchannel[0], guildchannel[1] );
@@ -85,11 +84,8 @@ async function cmd_get(lang, msg, args, line, wiki) {
 			var channelrole = ['Minimal Role:', '*unknown*'];
 			var channelinline = ['Inline commands:', '*unknown*'];
 			
-			return db.get( 'SELECT wiki, lang, role, inline FROM discord WHERE guild = ? AND (channel = ? OR channel = ? OR channel IS NULL) ORDER BY channel DESC', [channel.guildID, channel.id, '#' + ( channel.type === 'category' ? channel.id : channel.parentID )], (dberror, row) => {
-				if ( dberror ) {
-					console.log( '- Error while getting the settings: ' + dberror );
-				}
-				else if ( row ) {
+			return db.query( 'SELECT wiki, lang, role, inline FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', [channel.guildID, channel.id, '#' + ( channel.type === 'category' ? channel.id : channel.parentID )] ).then( ({rows:[row]}) => {
+				if ( row ) {
 					channellang[1] = row.lang;
 					channelwiki[1] = row.wiki;
 					channelrole[1] = ( row.role ? '`' + row.role + '` <@&' + row.role + '>' : '@everyone' );
@@ -101,7 +97,9 @@ async function cmd_get(lang, msg, args, line, wiki) {
 					channelrole[1] = '@everyone';
 					channelinline[1] = 'enabled';
 				}
-				
+			}, dberror => {
+				console.log( '- Error while getting the settings: ' + dberror );
+			} ).then( () => {
 				if ( msg.showEmbed() ) {
 					var text = '';
 					var 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] );

+ 7 - 9
cmds/verify.js

@@ -31,15 +31,7 @@ function cmd_verify(lang, msg, args, line, wiki, old_username = '') {
 	if ( wiki.isGamepedia() ) username = username.replace( /^userprofile\s*:/i, '' );
 	
 	var embed = new MessageEmbed().setFooter( lang.get('verify.footer') ).setTimestamp();
-	db.all( 'SELECT role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = ? AND channel LIKE ? ORDER BY configid ASC', [msg.guild.id, '%|' + msg.channel.id + '|%'], (dberror, rows) => {
-		if ( dberror || !rows ) {
-			console.log( '- Error while getting the verifications: ' + dberror );
-			embed.setColor('#000000').setDescription( lang.get('verify.error') );
-			msg.replyMsg( lang.get('verify.error_reply'), {embed}, false, false ).then( message => {
-				if ( message ) message.reactEmoji('error');
-			} );
-			return dberror;
-		}
+	db.query( 'SELECT role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [msg.guild.id, '%|' + msg.channel.id + '|%'] ).then( ({rows}) => {
 		if ( !rows.length ) {
 			if ( msg.onlyVerifyCommand ) return;
 			return msg.replyMsg( lang.get('verify.missing') + ( msg.isAdmin() ? '\n`' + ( patreons[msg.guild.id] || process.env.prefix ) + 'verification`' : '' ) );
@@ -378,6 +370,12 @@ function cmd_verify(lang, msg, args, line, wiki, old_username = '') {
 			
 			if ( reaction ) reaction.removeEmoji();
 		} ) );
+	}, dberror => {
+		console.log( '- Error while getting the verifications: ' + dberror );
+		embed.setColor('#000000').setDescription( lang.get('verify.error') );
+		msg.replyMsg( lang.get('verify.error_reply'), {embed}, false, false ).then( message => {
+			if ( message ) message.reactEmoji('error');
+		} );
 	} );
 }
 

+ 7 - 11
cmds/voice.js

@@ -21,22 +21,15 @@ function cmd_voice(lang, msg, args, line, wiki) {
 			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.guild.id] ? null : 1 );
-			return db.run( 'UPDATE discord SET voice = ? WHERE guild = ? AND channel IS NULL', [value, msg.guild.id], function (dberror) {
-				if ( dberror ) {
-					console.log( '- Error while editing the voice settings: ' + dberror );
-					msg.replyMsg( lang.get('settings.save_failed'), {}, true );
-					return dberror;
-				}
+			return db.query( 'UPDATE discord SET voice = $1 WHERE guild = $2 AND channel IS NULL', [value, msg.guild.id] ).then( () => {
 				console.log( '- Voice settings successfully updated.' );
 				if ( value ) {
 					voice[msg.guild.id] = lang.lang;
-					db.get( 'SELECT lang FROM discord WHERE guild = ? AND channel IS NULL', [msg.guild.id], (error, row) => {
-						if ( error ) {
-							console.log( '- Error while getting the voice language: ' + error );
-							return error;
-						}
+					db.query( 'SELECT lang FROM discord WHERE guild = ? AND channel IS NULL', [msg.guild.id] ).then( ({rows:[row]}) => {
 						console.log( '- Voice language successfully updated.' );
 						voice[msg.guild.id] = 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 );
 				}
@@ -44,6 +37,9 @@ function cmd_voice(lang, msg, args, line, wiki) {
 					delete voice[msg.guild.id];
 					msg.replyMsg( lang.get('voice.disabled'), {}, true );
 				}
+			}, dberror => {
+				console.log( '- Error while editing the voice settings: ' + dberror );
+				msg.replyMsg( lang.get('settings.save_failed'), {}, true );
 			} );
 		}
 	}

+ 3 - 6
dashboard/index.js

@@ -231,13 +231,10 @@ function graceful(signal) {
 	server.close( () => {
 		console.log( '- Dashboard: ' + signal + ': Closed the dashboard server.' );
 	} );
-	db.close( dberror => {
-		if ( dberror ) {
-			console.log( '- Dashboard: ' + signal + ': Error while closing the database connection: ' + dberror );
-			return dberror;
-		}
+	db.end().then( () => {
 		console.log( '- Dashboard: ' + signal + ': Closed the database connection.' );
-		process.exit(0);
+	}, dberror => {
+		console.log( '- Dashboard: ' + signal + ': Error while closing the database connection: ' + dberror );
 	} );
 }
 

+ 12 - 20
dashboard/settings.js

@@ -171,17 +171,7 @@ function createForm($, header, dashboardLang, settings, guildRoles, guildChannel
  * @param {import('./i18n.js')} dashboardLang - The user language
  */
 function dashboard_settings(res, $, guild, args, dashboardLang) {
-	db.all( 'SELECT channel, wiki, lang, role, inline, prefix, patreon FROM discord WHERE guild = ? ORDER BY channel ASC', [guild.id], function(dberror, rows) {
-		if ( dberror ) {
-			console.log( '- Dashboard: Error while getting the settings: ' + dberror );
-			createNotice($, 'error', dashboardLang);
-			$('<p>').text(dashboardLang.get('settings.failed')).appendTo('#text .description');
-			$('.channel#settings').addClass('selected');
-			let body = $.html();
-			res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
-			res.write( body );
-			return res.end();
-		}
+	db.query( 'SELECT channel, wiki, lang, role, inline, prefix, patreon FROM discord WHERE guild = $1 ORDER BY channel ASC', [guild.id] ).then( ({rows}) => {
 		$('<p>').html(dashboardLang.get('settings.desc', true, $('<code>').text(guild.name))).appendTo('#text .description');
 		if ( !rows.length ) {
 			$('.channel#settings').addClass('selected');
@@ -190,10 +180,7 @@ function dashboard_settings(res, $, guild, args, dashboardLang) {
 				wiki: defaultSettings.wiki,
 				lang: ( guild.locale || defaultSettings.lang )
 			}, guild.roles).attr('action', `/guild/${guild.id}/settings/default`).appendTo('#text');
-			let body = $.html();
-			res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
-			res.write( body );
-			return res.end();
+			return;
 		}
 		let isPatreon = rows.some( row => row.patreon );
 		let channellist = rows.filter( row => row.channel ).map( row => {
@@ -251,6 +238,12 @@ function dashboard_settings(res, $, guild, args, dashboardLang) {
 			$('.channel#settings').addClass('selected');
 			createForm($, dashboardLang.get('settings.form.default'), dashboardLang, rows.find( row => !row.channel ), guild.roles).attr('action', `/guild/${guild.id}/settings/default`).appendTo('#text');
 		}
+	}, dberror => {
+		console.log( '- Dashboard: Error while getting the settings: ' + dberror );
+		createNotice($, 'error', dashboardLang);
+		$('<p>').text(dashboardLang.get('settings.failed')).appendTo('#text .description');
+		$('.channel#settings').addClass('selected');
+	} ).then( () => {
 		let body = $.html();
 		res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
 		res.write( body );
@@ -312,14 +305,13 @@ function update_settings(res, userSettings, guild, type, settings) {
 			userSettings.guilds.isMember.delete(guild);
 			return res('/', 'savefail');
 		}
-		if ( response.message === 'noChannel' ) return db.run( 'DELETE FROM discord WHERE guild = ? AND ( channel = ? OR channel = ? )', [guild, type, `#${type}`], function (delerror) {
-			if ( delerror ) {
-				console.log( '- Dashboard: Error while removing the settings: ' + delerror );
-				return res(`/guild/${guild}/settings`, 'savefail');
-			}
+		if ( response.message === 'noChannel' ) return db.query( 'DELETE FROM discord WHERE guild = $1 AND ( channel = $2 OR channel = $3 )', [guild, type, `#${type}`] ).then( () => {
 			console.log( `- Dashboard: Settings successfully removed: ${guild}#${type}` );
 			if ( settings.delete_settings ) return res(`/guild/${guild}/settings`, 'save');
 			else return res(`/guild/${guild}/settings`, 'savefail');
+		}, delerror =>{
+			console.log( '- Dashboard: Error while removing the settings: ' + delerror );
+			return res(`/guild/${guild}/settings`, 'savefail');
 		} );
 		if ( type !== 'default' && !hasPerm(response.userPermissions, 'VIEW_CHANNEL', 'SEND_MESSAGES') ) {
 			return res(`/guild/${guild}/settings/${type}`, 'savefail');

+ 4 - 13
dashboard/util.js

@@ -6,19 +6,10 @@ const got = require('got').extend( {
 	},
 	responseType: 'json'
 } );
-const sqlite3 = require('sqlite3').verbose();
-const mode = ( process.env.READONLY ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE );
-const db = new sqlite3.Database( './wikibot.db', mode, dberror => {
-	if ( dberror ) {
-		console.log( '- Dashboard: Error while connecting to the database: ' + dberror );
-		return dberror;
-	}
-	db.exec( 'PRAGMA foreign_keys = ON;', function (error) {
-		if ( error ) {
-			console.log( '- Dashboard: Error while enabling the foreign key constraint: ' + error );
-		}
-		console.log( '- Dashboard: Connected to the database.' );
-	} );
+const {Pool} = require('pg');
+const db = new Pool();
+db.on( 'error', dberror => {
+	console.log( '- Dashboard: Error while connecting to the database: ' + dberror );
 } );
 
 /**

+ 66 - 186
database.js

@@ -1,6 +1,9 @@
 const {defaultSettings} = require('./util/default.json');
-const sqlite3 = require('sqlite3').verbose();
-const mode = ( process.env.READONLY ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE );
+const {Client} = require('pg');
+const db = new Client();
+db.on( 'error', dberror => {
+	console.log( '- Error while connecting to the database: ' + dberror );
+} );
 
 const schema = [`
 BEGIN TRANSACTION;
@@ -23,13 +26,13 @@ CREATE TABLE discord (
                     REFERENCES discord (main) ON DELETE CASCADE,
     channel TEXT,
     wiki    TEXT    NOT NULL
-                    DEFAULT [${defaultSettings.wiki}],
+                    DEFAULT '${defaultSettings.wiki}',
     lang    TEXT    NOT NULL
-                    DEFAULT [${defaultSettings.lang}],
+                    DEFAULT '${defaultSettings.lang}',
     role    TEXT,
     inline  INTEGER,
     prefix  TEXT    NOT NULL
-                    DEFAULT [${process.env.prefix}],
+                    DEFAULT '${process.env.prefix}',
     patreon TEXT    REFERENCES patreons (patreon) ON DELETE SET NULL,
     voice   INTEGER,
     UNIQUE (
@@ -41,6 +44,7 @@ CREATE TABLE discord (
 CREATE INDEX idx_discord_channel ON discord (
     guild,
     channel DESC
+            NULLS LAST
 );
 
 CREATE INDEX idx_discord_patreon ON discord (
@@ -60,14 +64,14 @@ CREATE TABLE verification (
     channel    TEXT    NOT NULL,
     role       TEXT    NOT NULL,
     editcount  INTEGER NOT NULL
-                       DEFAULT [0],
-    postcount  INTEGER DEFAULT [0],
+                       DEFAULT 0,
+    postcount  INTEGER DEFAULT 0,
     usergroup  TEXT    NOT NULL
-                       DEFAULT [user],
+                       DEFAULT 'user',
     accountage INTEGER NOT NULL
-                       DEFAULT [0],
+                       DEFAULT 0,
     rename     INTEGER NOT NULL
-                       DEFAULT [0],
+                       DEFAULT 0,
     UNIQUE (
         guild,
         configid
@@ -88,11 +92,11 @@ CREATE TABLE rcgcdw (
                      UNIQUE,
     wiki     TEXT    NOT NULL,
     lang     TEXT    NOT NULL
-                     DEFAULT [${defaultSettings.lang}],
+                     DEFAULT '${defaultSettings.lang}',
     display  INTEGER NOT NULL
-                     DEFAULT [1],
+                     DEFAULT 1,
     rcid     INTEGER,
-    postid   TEXT    DEFAULT [-1],
+    postid   TEXT    DEFAULT '-1',
     UNIQUE (
         guild,
         configid
@@ -123,182 +127,58 @@ CREATE INDEX idx_blocklist_wiki ON blocklist (
 );
 
 COMMIT TRANSACTION;
-PRAGMA user_version = 3;
-`,
-`
-BEGIN TRANSACTION;
-
-PRAGMA foreign_keys = OFF;
-
-UPDATE rcgcdw SET postid = '-1' WHERE wikiid IS NULL;
-
-CREATE TABLE rcgcdw_temp_table AS SELECT * FROM rcgcdw;
-
-DROP TABLE rcgcdw;
-
-CREATE TABLE rcgcdw (
-    guild    TEXT    NOT NULL
-                     REFERENCES discord (main) ON DELETE CASCADE,
-    configid INTEGER NOT NULL,
-    webhook  TEXT    NOT NULL
-                     UNIQUE,
-    wiki     TEXT    NOT NULL,
-    lang     TEXT    NOT NULL
-                     DEFAULT [${defaultSettings.lang}],
-    display  INTEGER NOT NULL
-                     DEFAULT [1],
-    rcid     INTEGER,
-    postid   TEXT    DEFAULT [-1],
-    UNIQUE (
-        guild,
-        configid
-    )
-);
-
-INSERT INTO rcgcdw (
-    guild,
-    configid,
-    webhook,
-    wiki,
-    lang,
-    display,
-    rcid,
-    postid
-)
-SELECT guild,
-       configid,
-       webhook,
-       wiki,
-       lang,
-       display,
-       rcid,
-       postid
-FROM rcgcdw_temp_table;
-
-DROP TABLE rcgcdw_temp_table;
-
-CREATE INDEX idx_rcgcdw_wiki ON rcgcdw (
-    wiki
-);
-
-CREATE INDEX idx_rcgcdw_webhook ON rcgcdw (
-    webhook
-);
-
-CREATE INDEX idx_rcgcdw_config ON rcgcdw (
-    guild,
-    configid ASC
-);
-
-COMMIT TRANSACTION;
-PRAGMA user_version = 2;
-`,
-`
-BEGIN TRANSACTION;
-
-PRAGMA foreign_keys = OFF;
-
-CREATE TABLE verification_temp_table AS SELECT * FROM verification;
-
-DROP TABLE verification;
-
-CREATE TABLE verification (
-    guild      TEXT    NOT NULL
-                       REFERENCES discord (main) ON DELETE CASCADE,
-    configid   INTEGER NOT NULL,
-    channel    TEXT    NOT NULL,
-    role       TEXT    NOT NULL,
-    editcount  INTEGER NOT NULL
-                       DEFAULT [0],
-    postcount  INTEGER DEFAULT [0],
-    usergroup  TEXT    NOT NULL
-                       DEFAULT [user],
-    accountage INTEGER NOT NULL
-                       DEFAULT [0],
-    rename     INTEGER NOT NULL
-                       DEFAULT [0],
-    UNIQUE (
-        guild,
-        configid
-    )
-);
-
-INSERT INTO verification (
-    guild,
-    configid,
-    channel,
-    role,
-    editcount,
-    usergroup,
-    accountage,
-    rename
-)
-SELECT guild,
-       configid,
-       channel,
-       role,
-       editcount,
-       usergroup,
-       accountage,
-       rename
-FROM verification_temp_table;
-
-DROP TABLE verification_temp_table;
-
-CREATE INDEX idx_verification_config ON verification (
-    guild,
-    configid ASC,
-    channel
-);
-
-COMMIT TRANSACTION;
-PRAGMA user_version = 3;
+ALTER DATABASE "${process.env.PGDATABASE}" SET my.version TO 1;
 `];
 
-module.exports = new Promise( (resolve, reject) => {
-	const db = new sqlite3.Database( './wikibot.db', mode, dberror => {
-		if ( dberror ) {
-			console.log( '- Error while connecting to the database: ' + dberror );
-			return reject();
+module.exports = db.connect().then( () => {
+	return db.query( 'SELECT CURRENT_SETTING($1) AS version', ['my.version'] ).then( ({rows:[row]}) => {
+		row.version = parseInt(row.version, 10);
+		if ( row.version > schema.length ) {
+			console.log( '- Invalid database version: v' + row.version );
+			return Promise.reject();
 		}
-		db.get( 'PRAGMA user_version;', (error, row) => {
-			if ( error ) {
-				console.log( '- Error while getting the database version: ' + error );
-				return reject();
-			}
-			if ( row.user_version > schema.length ) {
-				console.log( '- Invalid database version: v' + row.user_version );
-				return reject();
-			}
-			if ( row.user_version === schema.length ) {
-				console.log( '- The database is up to date: v' + row.user_version );
-				db.close( cerror => {
-					if ( cerror ) {
-						console.log( '- Error while closing the database connection: ' + cerror );
-						return cerror;
-					}
-				} );
-				return resolve();
-			}
-			console.log( '- The database outdated: v' + row.user_version );
-			if ( process.env.READONLY ) return reject();
-			db.exec( schema.filter( (sql, version) => {
-				if ( row.user_version === 0 ) return ( version === 0 );
-				return ( row.user_version <= version );
-			} ).join('\n'), exerror => {
-				if ( exerror ) {
-					console.log( '- Error while updating the database: ' + exerror );
-					return reject();
-				}
+		if ( row.version === schema.length ) {
+			console.log( '- The database is up to date: v' + row.version );
+			return;
+		}
+		console.log( '- The database is outdated: v' + row.version );
+		if ( process.env.READONLY ) return Promise.reject();
+		return db.query( schema.filter( (sql, version) => {
+			if ( row.version === 0 ) return ( version === 0 );
+			return ( row.version <= version );
+		} ).join('\n') ).then( () => {
+			console.log( '- The database has been updated to: v' + schema.length );
+		}, dberror => {
+			console.log( '- Error while updating the database: ' + dberror );
+			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 );
-				db.close( cerror => {
-					if ( cerror ) {
-						console.log( '- Error while closing the database connection: ' + cerror );
-						return cerror;
-					}
-				} );
-				return resolve();
+			}, dberror => {
+				console.log( '- Error while updating the database: ' + dberror );
+				return Promise.reject();
 			} );
-		} );
+		}
+		console.log( '- Error while getting the database version: ' + dberror );
+		return Promise.reject();
 	} );
-} );
+}, dberror => {
+	console.log( '- Error while connecting to the database: ' + dberror );
+	return Promise.reject();
+} ).then( () => {
+	db.end().then( () => {
+		console.log( '- Closed the database connection.' );
+	}, dberror => {
+		console.log( '- Error while closing the database connection: ' + dberror );
+	} );
+}, () => {
+	return db.end().then( () => {
+		console.log( '- Closed the database connection.' );
+	}, dberror => {
+		console.log( '- Error while closing the database connection: ' + dberror );
+	} ).then( () => {
+		return Promise.reject();
+	} );
+} );

File diff suppressed because it is too large
+ 449 - 38
package-lock.json


+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "got": "^11.8.1",
     "htmlparser2": "^6.0.0",
     "npm": "^7.5.3",
+    "pg": "^8.5.1",
     "sqlite3": "^5.0.0"
   },
   "repository": {

+ 17 - 67
util/database.js

@@ -1,74 +1,24 @@
-const sqlite3 = require('sqlite3').verbose();
-const mode = ( process.env.READONLY ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE );
-const db = new sqlite3.Database( './wikibot.db', mode, dberror => {
-	if ( dberror ) {
-		console.log( '- ' + shardId + ': Error while connecting to the database: ' + dberror );
-		return dberror;
-	}
-	db.exec( 'PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 1000;', function (error) {
-		if ( error ) {
-			console.log( '- ' + shardId + ': Error while enabling the foreign key constraint: ' + error );
-		}
-		console.log( '- ' + shardId + ': Connected to the database.' );
-		getSettings();
-	} );
+const {Pool} = require('pg');
+const db = new Pool();
+db.on( 'error', dberror => {
+	console.log( '- ' + shardId + ': Error while connecting to the database: ' + dberror );
 } );
 
-/**
- * Fill the patreon list.
- * @param {Number} [trysettings] - The amount of tries.
- */
-function getSettings(trysettings = 1) {
-	db.each( 'SELECT guild, prefix FROM discord WHERE patreon IS NOT NULL', [], (dberror, row) => {
-		if ( dberror ) {
-			console.log( '- ' + shardId + ': ' + trysettings + '. Error while getting the patreon: ' + dberror );
-				if ( trysettings < 10 ) {
-					trysettings++;
-					getSettings(trysettings);
-				}
-			return dberror;
-		}
+db.query( 'SELECT guild, prefix FROM discord WHERE patreon IS NOT NULL' ).then( ({rows}) => {
+	console.log( '- ' + shardId + ': Patreons successfully loaded.' );
+	rows.forEach( row => {
 		patreons[row.guild] = row.prefix;
-	}, (dberror) => {
-		if ( dberror ) {
-			console.log( '- ' + trysettings + '. Error while getting the patreons: ' + dberror );
-			if ( trysettings < 10 ) {
-				trysettings++;
-				getSettings(trysettings);
-			}
-			return dberror;
-		}
-		console.log( '- ' + shardId + ': Patreons successfully loaded.' );
-		getVoice();
 	} );
-}
-
-/**
- * Fill the voice list.
- * @param {Number} [trysettings] - The amount of tries.
- */
-function getVoice(trysettings = 1) {
-	db.each( 'SELECT guild, lang FROM discord WHERE voice IS NOT NULL', [], (dberror, row) => {
-		if ( dberror ) {
-			console.log( '- ' + shardId + ': ' + trysettings + '. Error while getting the voice channel: ' + dberror );
-			if ( trysettings < 10 ) {
-				trysettings++;
-				getVoice(trysettings);
-			}
-			return dberror;
-		}
+}, dberror => {
+	console.log( '- ' + shardId + ': Error while getting the patreons: ' + dberror );
+} );
+db.query( 'SELECT guild, lang FROM discord WHERE voice IS NOT NULL' ).then( ({rows}) => {
+	console.log( '- ' + shardId + ': Voice channels successfully loaded.' );
+	rows.forEach( row => {
 		voice[row.guild] = row.lang;
-	}, (dberror) => {
-		if ( dberror ) {
-			console.log( '- ' + shardId + ': ' + trysettings + '. Error while getting the voice channels: ' + dberror );
-			if ( trysettings < 10 ) {
-				trysettings++;
-				getVoice(trysettings);
-			}
-			return dberror;
-		}
-		console.log( '- ' + shardId + ': Voice channels successfully loaded.' );
 	} );
-}
+}, dberror => {
+	console.log( '- ' + shardId + ': Error while getting the voice channels: ' + dberror );
+} );
 
-module.exports = db;
+module.exports = db;

Some files were not shown because too many files changed in this diff