Added support for database updates/migration, added guild access timestamps
This commit is contained in:
		
							parent
							
								
									55f6271cd9
								
							
						
					
					
						commit
						c568423870
					
				
					 5 changed files with 106 additions and 39 deletions
				
			
		| 
						 | 
				
			
			@ -100,6 +100,7 @@ export default async (client, cluster, worker, ipc, message) => {
 | 
			
		|||
  try {
 | 
			
		||||
    await database.addCount(aliases.get(command) ?? command);
 | 
			
		||||
    const startTime = new Date();
 | 
			
		||||
    await database.updateTime(startTime, message.channel.guild.id);
 | 
			
		||||
    // eslint-disable-next-line no-unused-vars
 | 
			
		||||
    const commandClass = new cmd(client, cluster, worker, ipc, message, parsed._, message.content.substring(prefix.length).trim().replace(command, "").trim(), (({ _, ...o }) => o)(parsed)); // we also provide the message content as a parameter for cases where we need more accuracy
 | 
			
		||||
    const result = await commandClass.run();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								shard.js
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								shard.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -44,6 +44,8 @@ class Shard extends BaseClusterWorker {
 | 
			
		|||
    }
 | 
			
		||||
    log("info", "Finished loading commands.");
 | 
			
		||||
 | 
			
		||||
    await database.setup(this.ipc);
 | 
			
		||||
 | 
			
		||||
    // register events
 | 
			
		||||
    log("info", `Attempting to load events...`);
 | 
			
		||||
    for await (const file of this.getFiles("./events/")) {
 | 
			
		||||
| 
						 | 
				
			
			@ -103,8 +105,6 @@ class Shard extends BaseClusterWorker {
 | 
			
		|||
    // connect to lavalink
 | 
			
		||||
    if (!status && !connected) connect(this.bot);
 | 
			
		||||
 | 
			
		||||
    database.setup();
 | 
			
		||||
 | 
			
		||||
    this.activityChanger();
 | 
			
		||||
 | 
			
		||||
    log("info", `Started worker ${this.workerID}.`);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { prefixCache, disabledCmdCache, disabledCache, commands } from "../collections.js";
 | 
			
		||||
import { error, log } from "../logger.js";
 | 
			
		||||
import * as logger from "../logger.js";
 | 
			
		||||
 | 
			
		||||
import Postgres from "pg";
 | 
			
		||||
const connection = new Postgres.Pool({
 | 
			
		||||
| 
						 | 
				
			
			@ -7,10 +7,74 @@ const connection = new Postgres.Pool({
 | 
			
		|||
  statement_timeout: 10000
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const psqlUpdates = [
 | 
			
		||||
  "", // reserved
 | 
			
		||||
  "CREATE TABLE settings ( id smallint PRIMARY KEY, version integer NOT NULL, CHECK(id = 1) );\nALTER TABLE guilds ADD COLUMN accessed timestamp;"
 | 
			
		||||
];
 | 
			
		||||
// INSERT INTO settings (id, version) VALUES(1, $1)
 | 
			
		||||
export async function setup(ipc) {
 | 
			
		||||
  let version;
 | 
			
		||||
  try {
 | 
			
		||||
    version = (await connection.query("SELECT version FROM settings WHERE id = 1")).rows[0].version;
 | 
			
		||||
  } catch {
 | 
			
		||||
    version = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (version < (psqlUpdates.length - 1)) {
 | 
			
		||||
    logger.warn(`Migrating PostgreSQL database, which is currently at version ${version}...`);
 | 
			
		||||
    await connection.query("BEGIN");
 | 
			
		||||
    try {
 | 
			
		||||
      while (version < (psqlUpdates.length - 1)) {
 | 
			
		||||
        version++;
 | 
			
		||||
        logger.warn(`Running version ${version} update script (${psqlUpdates[version]})...`);
 | 
			
		||||
        await connection.query(psqlUpdates[version]);
 | 
			
		||||
      }
 | 
			
		||||
      await connection.query("COMMIT");
 | 
			
		||||
      await connection.query("INSERT INTO settings (id, version) VALUES (1, $1) ON CONFLICT (id) DO NOTHING", [psqlUpdates.length - 1]);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.error(`PostgreSQL migration failed: ${e}`);
 | 
			
		||||
      await connection.query("ROLLBACK");
 | 
			
		||||
      logger.error("Unable to start the bot, quitting now.");
 | 
			
		||||
      throw ipc.totalShutdown();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let counts;
 | 
			
		||||
  try {
 | 
			
		||||
    counts = await connection.query("SELECT * FROM counts");
 | 
			
		||||
  } catch {
 | 
			
		||||
    counts = { rows: [] };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!counts.rows[0]) {
 | 
			
		||||
    for (const command of commands.keys()) {
 | 
			
		||||
      await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    const exists = [];
 | 
			
		||||
    for (const command of commands.keys()) {
 | 
			
		||||
      const count = await connection.query("SELECT * FROM counts WHERE command = $1", [command]);
 | 
			
		||||
      if (!count.rows[0]) {
 | 
			
		||||
        await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]);
 | 
			
		||||
      }
 | 
			
		||||
      exists.push(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const { command } of counts.rows) {
 | 
			
		||||
      if (!exists.includes(command)) {
 | 
			
		||||
        await connection.query("DELETE FROM counts WHERE command = $1", [command]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function getGuild(query) {
 | 
			
		||||
  return (await connection.query("SELECT * FROM guilds WHERE guild_id = $1", [query])).rows[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function updateTime(time, guild) {
 | 
			
		||||
  await connection.query("UPDATE guilds SET accessed = $1 WHERE guild_id = $2", [time, guild]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function setPrefix(prefix, guild) {
 | 
			
		||||
  await connection.query("UPDATE guilds SET prefix = $1 WHERE guild_id = $2", [prefix, guild.id]);
 | 
			
		||||
  prefixCache.set(guild.id, prefix);
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +157,7 @@ export async function addGuild(guild) {
 | 
			
		|||
  try {
 | 
			
		||||
    await connection.query("INSERT INTO guilds (guild_id, prefix, disabled, disabled_commands) VALUES ($1, $2, $3, $4)", [guild.id, process.env.PREFIX, [], []]);
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    error(`Failed to register guild ${guild.id}: ${e}`);
 | 
			
		||||
    logger.error(`Failed to register guild ${guild.id}: ${e}`);
 | 
			
		||||
  }
 | 
			
		||||
  return await this.getGuild(guild.id);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -101,41 +165,11 @@ export async function addGuild(guild) {
 | 
			
		|||
export async function fixGuild(guild) {
 | 
			
		||||
  const guildDB = await connection.query("SELECT exists(SELECT 1 FROM guilds WHERE guild_id = $1)", [guild.id]);
 | 
			
		||||
  if (!guildDB.rows[0].exists) {
 | 
			
		||||
    log(`Registering guild database entry for guild ${guild.id}...`);
 | 
			
		||||
    logger.log(`Registering guild database entry for guild ${guild.id}...`);
 | 
			
		||||
    return await this.addGuild(guild);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function setup() {
 | 
			
		||||
  let counts;
 | 
			
		||||
  try {
 | 
			
		||||
    counts = await connection.query("SELECT * FROM counts");
 | 
			
		||||
  } catch {
 | 
			
		||||
    counts = { rows: [] };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!counts.rows[0]) {
 | 
			
		||||
    for (const command of commands.keys()) {
 | 
			
		||||
      await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    const exists = [];
 | 
			
		||||
    for (const command of commands.keys()) {
 | 
			
		||||
      const count = await connection.query("SELECT * FROM counts WHERE command = $1", [command]);
 | 
			
		||||
      if (!count.rows[0]) {
 | 
			
		||||
        await connection.query("INSERT INTO counts (command, count) VALUES ($1, $2)", [command, 0]);
 | 
			
		||||
      }
 | 
			
		||||
      exists.push(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const { command } of counts.rows) {
 | 
			
		||||
      if (!exists.includes(command)) {
 | 
			
		||||
        await connection.query("DELETE FROM counts WHERE command = $1", [command]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function stop() {
 | 
			
		||||
  await connection.end();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,32 @@ import * as logger from "../logger.js";
 | 
			
		|||
import sqlite3 from "better-sqlite3";
 | 
			
		||||
const connection = sqlite3(process.env.DB.replace("sqlite://", ""));
 | 
			
		||||
 | 
			
		||||
export async function setup() {
 | 
			
		||||
const sqliteUpdates = [
 | 
			
		||||
  "", // reserved
 | 
			
		||||
  "ALTER TABLE guilds ADD COLUMN accessed int" // CREATE TABLE settings ( version int );\n
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export async function setup(ipc) {
 | 
			
		||||
  let version = connection.pragma("user_version", { simple: true });
 | 
			
		||||
  if (version < (sqliteUpdates.length - 1)) {
 | 
			
		||||
    logger.warn(`Migrating SQLite database at ${process.env.DB}, which is currently at version ${version}...`);
 | 
			
		||||
    connection.prepare("BEGIN TRANSACTION").run();
 | 
			
		||||
    try {
 | 
			
		||||
      while (version < (sqliteUpdates.length - 1)) {
 | 
			
		||||
        version++;
 | 
			
		||||
        logger.warn(`Running version ${version} update script (${sqliteUpdates[version]})...`);
 | 
			
		||||
        connection.prepare(sqliteUpdates[version]).run();
 | 
			
		||||
      }
 | 
			
		||||
      connection.pragma(`user_version = ${version}`); // insecure, but the normal templating method doesn't seem to work here
 | 
			
		||||
      connection.prepare("COMMIT").run();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      logger.error(`SQLite migration failed: ${e}`);
 | 
			
		||||
      connection.prepare("ROLLBACK").run();
 | 
			
		||||
      logger.error("Unable to start the bot, quitting now.");
 | 
			
		||||
      ipc.totalShutdown();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let counts;
 | 
			
		||||
  try {
 | 
			
		||||
    counts = connection.prepare("SELECT * FROM counts").all();
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +83,10 @@ export async function fixGuild(guild) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function updateTime(time, guild) {
 | 
			
		||||
  connection.prepare("UPDATE guilds SET accessed = ? WHERE guild_id = ?").run(Math.floor(time / 1000), guild);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function addCount(command) {
 | 
			
		||||
  connection.prepare("UPDATE counts SET count = count + 1 WHERE command = ?").run(command);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,11 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
 | 
			
		||||
    CREATE TABLE guilds ( guild_id VARCHAR(30) NOT NULL PRIMARY KEY, prefix VARCHAR(15) NOT NULL, disabled text ARRAY NOT NULL, disabled_commands text ARRAY NOT NULL );
 | 
			
		||||
    CREATE TABLE guilds ( guild_id VARCHAR(30) NOT NULL PRIMARY KEY, prefix VARCHAR(15) NOT NULL, disabled text ARRAY NOT NULL, disabled_commands text ARRAY NOT NULL, accessed timestamp );
 | 
			
		||||
    CREATE TABLE counts ( command VARCHAR NOT NULL PRIMARY KEY, count integer NOT NULL );
 | 
			
		||||
    CREATE TABLE tags ( guild_id VARCHAR(30) NOT NULL, name text NOT NULL, content text NOT NULL, author VARCHAR(30) NOT NULL, UNIQUE(guild_id, name) );
 | 
			
		||||
 | 
			
		||||
    CREATE TABLE settings ( id smallint PRIMARY KEY, version integer NOT NULL, CHECK(id = 1) );
 | 
			
		||||
    INSERT INTO settings (id, version) VALUES (1, 1) ON CONFLICT (id) DO NOTHING;
 | 
			
		||||
EOSQL
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue