From c2a2f160232390c9130127283015a53a5719eb05 Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Fri, 16 Jun 2023 19:57:29 -0400 Subject: [PATCH 1/9] feat: pre-released postgreSQL driver for quick.db --- package.json | 3 +- utils/pgsqlDriver.js | 109 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 utils/pgsqlDriver.js diff --git a/package.json b/package.json index b781bae6..a47d61a7 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "fs-extra": "^11.1.1", "jsonc": "^2.0.0", "mysql2": "^3.2.0", - "quick.db": "^9.1.3", + "pg": "^8.11.0", + "quick.db": "^9.1.6", "readline": "^1.3.0", "ticket-bot-transcript-uploader": "^1.3.0", "websocket": "^1.0.34" diff --git a/utils/pgsqlDriver.js b/utils/pgsqlDriver.js new file mode 100644 index 00000000..f9db5468 --- /dev/null +++ b/utils/pgsqlDriver.js @@ -0,0 +1,109 @@ +/** + * Official pre-released quick.db Postgresql driver. Remove this file when the driver is officially released ~ zhiyan114 + * Why add this? PostgreSQL is a better database than MySQL. Alternative would be to use Prisma, + * but I don't want to esentially re-write the database logic and cause confusions. + * Source: https://github.com/plexidev/quick.db/blob/dev/src/drivers/PostgresDriver.ts + */ + +const { Client } = require("pg"); + +module.exports = class PostgresDriver { + instance; + config; + conn; + + constructor(config) { + this.config = config; + } + + static createSingleton(config) { + if (!this.instance) this.instance = new PostgresDriver(config); + return this.instance; + } + + async connect() { + this.conn = new Client(this.config); + await this.conn.connect(); + } + + async disconnect() { + this.checkConnection(); + await this.conn.end(); + } + + checkConnection(){ + if (!this.conn) { + throw new Error("No connection to postgres database"); + } + } + + async prepare(table) { + this.checkConnection(); + await this.conn.query( + `CREATE TABLE IF NOT EXISTS ${table} (id VARCHAR(255), value TEXT)` + ); + } + + async getAllRows(table) { + this.checkConnection(); + const queryResult = await this.conn.query(`SELECT * FROM ${table}`); + return queryResult.rows.map((row) => ({ + id: row.id, + value: JSON.parse(row.value), + })); + } + + async getRowByKey( + table, + key + ) { + this.checkConnection(); + const queryResult = await this.conn.query( + `SELECT value FROM ${table} WHERE id = $1`, + [key] + ); + + if (queryResult.rowCount < 1) return [null, false]; + return [JSON.parse(queryResult.rows[0].value), true]; + } + + async setRowByKey( + table, + key, + value, + update + ) { + this.checkConnection(); + + const stringifiedValue = JSON.stringify(value); + + if (update) { + await this.conn.query( + `UPDATE ${table} SET value = $1 WHERE id = $2`, + [stringifiedValue, key] + ); + } else { + await this.conn.query( + `INSERT INTO ${table} (id, value) VALUES ($1, $2)`, + [key, stringifiedValue] + ); + } + + return value; + } + + async deleteAllRows(table) { + this.checkConnection(); + const queryResult = await this.conn.query(`DELETE FROM ${table}`); + return queryResult.rowCount; + } + + async deleteRowByKey(table, key) { + this.checkConnection(); + const queryResult = await this.conn.query( + `DELETE FROM ${table} WHERE id = $1`, + [key] + ); + return queryResult.rowCount; + } +}; \ No newline at end of file From 315ec4fd048d3ca7faa23ee4da987246fe41ef31 Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Fri, 16 Jun 2023 20:13:01 -0400 Subject: [PATCH 2/9] docs: Added license URL for compliance --- utils/pgsqlDriver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/pgsqlDriver.js b/utils/pgsqlDriver.js index f9db5468..56707f4f 100644 --- a/utils/pgsqlDriver.js +++ b/utils/pgsqlDriver.js @@ -3,6 +3,7 @@ * Why add this? PostgreSQL is a better database than MySQL. Alternative would be to use Prisma, * but I don't want to esentially re-write the database logic and cause confusions. * Source: https://github.com/plexidev/quick.db/blob/dev/src/drivers/PostgresDriver.ts + * LICENSE: https://github.com/plexidev/quick.db/blob/dev/LICENSE.md */ const { Client } = require("pg"); From 31f9d37f24bac653bba235ea9e4026887cab89c4 Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Fri, 16 Jun 2023 20:15:03 -0400 Subject: [PATCH 3/9] feat: PostgreSQL support --- config/config.example.jsonc | 15 ++++++++++++++- index.js | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/config/config.example.jsonc b/config/config.example.jsonc index c1eeb864..af9afc33 100644 --- a/config/config.example.jsonc +++ b/config/config.example.jsonc @@ -4,8 +4,21 @@ "mainColor": "f6c42f", // The hex color of the embeds by default "lang": "main", // If you want to set english please set "main" + /* + Pick a database driver, postgres will take priority over mysql (if both are enabled, which please don't do). + If neither option is enabled, SQLite will be used. + *PostgreSQL will be using default schema* + */ + "postgre": { + "enabled": false, + "host": "postgresql.example.com", + "user": "postgres", + "password": "password", + "database": "postgres", + "table": "table" + }, "mysql": { - "enabled": false, // If you want to use MySQL set to true, if not set to false to use SQLite + "enabled": false, "host": "mysql.example.com", // The host of the MySQL database "user": "mysql", // The user of the MySQL database "password": "password", // The password of the MySQL database diff --git a/index.js b/index.js index 3d6babc7..1212cce1 100644 --- a/index.js +++ b/index.js @@ -19,9 +19,8 @@ const path = require("node:path"); const { Client, Collection, GatewayIntentBits } = require("discord.js"); // eslint-disable-next-line node/no-missing-require, node/no-unpublished-require const { token } = require("./config/token.json"); -const { QuickDB, MySQLDriver } = require("quick.db"); +const { QuickDB, MySQLDriver } = require("quick.db");// Remove this once the driver is published to the package (quick.db) const jsonc = require("jsonc"); -const { exec } = require("child_process"); process.on("unhandledRejection", (reason, promise, a) => { console.log(reason, promise, a); @@ -39,6 +38,7 @@ process.stdout.write(` Connecting to Discord... `); +// Update Detector fetch("https://api.github.com/repos/Sayrix/Ticket-Bot/tags").then((res)=> { if(Math.floor(res.status / 100) !== 2) return console.warn("[Version Check] Failed to pull latest version from server"); res.json().then((json) => { @@ -68,7 +68,33 @@ client.config = jsonc.parse(fs.readFileSync(path.join(__dirname, "config/config. let db = null; -if (client.config.mysql?.enabled) { +if(client.config.postgre?.enabled) { + // PostgreSQL Support. + (async () => { + try { + require.resolve("pg"); + } catch (e) { + console.error("pg driver is not installed!\n\nPlease run \"npm i pg\" in the console!"); + throw e.code; + } + const PostgresDriver = require("./utils/pgsqlDriver"); + const pgsql = new PostgresDriver({ + host: client.config.postgre?.host, + user: client.config.postgre?.user, + password: client.config.postgre?.password, + database: client.config.postgre?.database, + }); + + await pgsql.connect(); + + db = new QuickDB({ + driver: pgsql, + table: client.config.postgre?.table ?? "json" + }); + client.db = db; + })(); +} else if (client.config.mysql?.enabled) { + // MySQL Support (async () => { try { require.resolve("mysql2"); @@ -94,6 +120,7 @@ if (client.config.mysql?.enabled) { client.db = db; })(); } else { + // SQLite Support db = new QuickDB(); client.db = db; } From 1ed60f848ac25f825526d88a331545cd38d2244b Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Fri, 16 Jun 2023 20:22:53 -0400 Subject: [PATCH 4/9] style: Remove unnecessary comment --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 1212cce1..bb3e7efb 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ const path = require("node:path"); const { Client, Collection, GatewayIntentBits } = require("discord.js"); // eslint-disable-next-line node/no-missing-require, node/no-unpublished-require const { token } = require("./config/token.json"); -const { QuickDB, MySQLDriver } = require("quick.db");// Remove this once the driver is published to the package (quick.db) +const { QuickDB, MySQLDriver } = require("quick.db"); const jsonc = require("jsonc"); process.on("unhandledRejection", (reason, promise, a) => { From 2168b2a574a6902d7cf38a8f18f6dc333641e570 Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Sat, 17 Jun 2023 21:48:29 -0400 Subject: [PATCH 5/9] fix(config): Replaced postgres's table name to json --- config/config.example.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.example.jsonc b/config/config.example.jsonc index af9afc33..d6109ea7 100644 --- a/config/config.example.jsonc +++ b/config/config.example.jsonc @@ -15,7 +15,7 @@ "user": "postgres", "password": "password", "database": "postgres", - "table": "table" + "table": "json" }, "mysql": { "enabled": false, From b5437fc540f970264914bb4cda67adaaf8c6040a Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Sun, 18 Jun 2023 15:40:05 -0400 Subject: [PATCH 6/9] fix(logs): #156 Create a webhook if no valid webhook is found --- utils/logs.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/utils/logs.js b/utils/logs.js index db274233..63d85fc3 100644 --- a/utils/logs.js +++ b/utils/logs.js @@ -25,12 +25,8 @@ module.exports = { .catch((e) => console.error("The channel to log events is not found!\n", e)); if (!channel) return console.error("The channel to log events is not found!"); - let webhooks = await channel.fetchWebhooks(); - if (webhooks.size === 0) { - await channel.createWebhook({ name: "Ticket Bot Logs" }); - webhooks = await channel.fetchWebhooks(); - } - const webhook = webhooks.find((wh) => wh.token); + const webhook = (await channel.fetchWebhooks()).find((wh) => wh.token) ?? + await channel.channel.createWebhook({ name: "Ticket Bot Logs" }); if (logsType === "ticketCreate") { const embed = new Discord.EmbedBuilder() From e966adfbccbc92eaf25df67536d30fc25f46bbd4 Mon Sep 17 00:00:00 2001 From: zhiyan114 Date: Sun, 18 Jun 2023 15:44:10 -0400 Subject: [PATCH 7/9] fix(ready): #156 validate the status url and set it to undefined if it's invalid instead of empty string --- events/ready.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/events/ready.js b/events/ready.js index 12bcb4b3..1c7dcc43 100644 --- a/events/ready.js +++ b/events/ready.js @@ -134,8 +134,9 @@ module.exports = { if (client.config.status.type && client.config.status.text) { // If the user just want to set the status but not the activity + const url = client.config.status.url; client.user.setPresence({ - activities: [{ name: client.config.status.text, type: type, url: client.config.status.url }], + activities: [{ name: client.config.status.text, type: type, url: (url && url.trim() !== "") ? url : undefined }], status: client.config.status.status, }); } From f79f2061895811174af2d70caefe40029234683b Mon Sep 17 00:00:00 2001 From: Sayrix <43046854+Sayrix@users.noreply.github.com> Date: Sun, 18 Jun 2023 21:59:18 +0200 Subject: [PATCH 8/9] Package updates & fixed eslint warnings --- config/config.example.jsonc | 10 +++++----- index.js | 26 ++++++++++++++++---------- package.json | 12 ++++++------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/config/config.example.jsonc b/config/config.example.jsonc index d6109ea7..7efa26a9 100644 --- a/config/config.example.jsonc +++ b/config/config.example.jsonc @@ -11,11 +11,11 @@ */ "postgre": { "enabled": false, - "host": "postgresql.example.com", - "user": "postgres", - "password": "password", - "database": "postgres", - "table": "json" + "host": "postgresql.example.com", // The host of the PostgreSQL database + "user": "postgres", // The user of the PostgreSQL database + "password": "password", // The password of the PostgreSQL database + "database": "postgres", // The name of the PostgreSQL database + "table": "json" // The name of the table where the tickets will be saved }, "mysql": { "enabled": false, diff --git a/index.js b/index.js index bb3e7efb..e8c2be2b 100644 --- a/index.js +++ b/index.js @@ -39,15 +39,19 @@ Connecting to Discord... `); // Update Detector -fetch("https://api.github.com/repos/Sayrix/Ticket-Bot/tags").then((res)=> { - if(Math.floor(res.status / 100) !== 2) return console.warn("[Version Check] Failed to pull latest version from server"); +fetch("https://api.github.com/repos/Sayrix/Ticket-Bot/tags").then((res) => { + if (Math.floor(res.status / 100) !== 2) return console.warn("[Version Check] Failed to pull latest version from server"); res.json().then((json) => { // Assumign the format stays consistent (i.e. x.x.x) - const latest = json[0].name.split(".").map(k=>parseInt(k)); - const current = require("./package.json").version.split(".").map(k=>parseInt(k)); - if(latest[0] > current[0] || - (latest[0] === current[0] && latest[1] > current[1]) || - (latest[0] === current[0] && latest[1] === current[1] && latest[2] > current[2])) + const latest = json[0].name.split(".").map((k) => parseInt(k)); + const current = require("./package.json") + .version.split(".") + .map((k) => parseInt(k)); + if ( + latest[0] > current[0] || + (latest[0] === current[0] && latest[1] > current[1]) || + (latest[0] === current[0] && latest[1] === current[1] && latest[2] > current[2]) + ) console.warn(`[Version Check] New version available: ${json[0].name}; Current Version: ${current.join(".")}`); else console.log("[Version Check] Up to date"); }); @@ -68,21 +72,22 @@ client.config = jsonc.parse(fs.readFileSync(path.join(__dirname, "config/config. let db = null; -if(client.config.postgre?.enabled) { +if (client.config.postgre?.enabled) { // PostgreSQL Support. (async () => { try { + // eslint-disable-next-line node/no-missing-require require.resolve("pg"); } catch (e) { console.error("pg driver is not installed!\n\nPlease run \"npm i pg\" in the console!"); throw e.code; } - const PostgresDriver = require("./utils/pgsqlDriver"); + const PostgresDriver = require("./utils/pgsqlDriver"); const pgsql = new PostgresDriver({ host: client.config.postgre?.host, user: client.config.postgre?.user, password: client.config.postgre?.password, - database: client.config.postgre?.database, + database: client.config.postgre?.database }); await pgsql.connect(); @@ -97,6 +102,7 @@ if(client.config.postgre?.enabled) { // MySQL Support (async () => { try { + // eslint-disable-next-line node/no-missing-require require.resolve("mysql2"); } catch (e) { console.error("mysql2 is not installed!\n\nPlease run \"npm i mysql2\" in the console!"); diff --git a/package.json b/package.json index a47d61a7..2c81615d 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,12 @@ }, "homepage": "https://github.com/Sayrix/ticket-bot#readme", "dependencies": { - "axios": "^1.3.4", - "better-sqlite3": "^8.2.0", - "discord.js": "^14.8.0", + "axios": "^1.4.0", + "better-sqlite3": "^8.4.0", + "discord.js": "^14.11.0", "fs-extra": "^11.1.1", "jsonc": "^2.0.0", - "mysql2": "^3.2.0", + "mysql2": "^3.3.5", "pg": "^8.11.0", "quick.db": "^9.1.6", "readline": "^1.3.0", @@ -39,12 +39,12 @@ "websocket": "^1.0.34" }, "devDependencies": { - "eslint": "^8.37.0", + "eslint": "^8.43.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.7" + "prettier": "^2.8.8" } } From cfc446567f5b2ce7444d970b0dc4fa66fbc863c8 Mon Sep 17 00:00:00 2001 From: Sayrix <43046854+Sayrix@users.noreply.github.com> Date: Sun, 18 Jun 2023 22:00:17 +0200 Subject: [PATCH 9/9] fixed eslint warnings --- utils/pgsqlDriver.js | 43 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/utils/pgsqlDriver.js b/utils/pgsqlDriver.js index 56707f4f..8a776e8f 100644 --- a/utils/pgsqlDriver.js +++ b/utils/pgsqlDriver.js @@ -6,6 +6,7 @@ * LICENSE: https://github.com/plexidev/quick.db/blob/dev/LICENSE.md */ +// eslint-disable-next-line node/no-missing-require const { Client } = require("pg"); module.exports = class PostgresDriver { @@ -32,7 +33,7 @@ module.exports = class PostgresDriver { await this.conn.end(); } - checkConnection(){ + checkConnection() { if (!this.conn) { throw new Error("No connection to postgres database"); } @@ -40,9 +41,7 @@ module.exports = class PostgresDriver { async prepare(table) { this.checkConnection(); - await this.conn.query( - `CREATE TABLE IF NOT EXISTS ${table} (id VARCHAR(255), value TEXT)` - ); + await this.conn.query(`CREATE TABLE IF NOT EXISTS ${table} (id VARCHAR(255), value TEXT)`); } async getAllRows(table) { @@ -50,44 +49,27 @@ module.exports = class PostgresDriver { const queryResult = await this.conn.query(`SELECT * FROM ${table}`); return queryResult.rows.map((row) => ({ id: row.id, - value: JSON.parse(row.value), + value: JSON.parse(row.value) })); } - async getRowByKey( - table, - key - ) { + async getRowByKey(table, key) { this.checkConnection(); - const queryResult = await this.conn.query( - `SELECT value FROM ${table} WHERE id = $1`, - [key] - ); + const queryResult = await this.conn.query(`SELECT value FROM ${table} WHERE id = $1`, [key]); if (queryResult.rowCount < 1) return [null, false]; return [JSON.parse(queryResult.rows[0].value), true]; } - async setRowByKey( - table, - key, - value, - update - ) { + async setRowByKey(table, key, value, update) { this.checkConnection(); const stringifiedValue = JSON.stringify(value); if (update) { - await this.conn.query( - `UPDATE ${table} SET value = $1 WHERE id = $2`, - [stringifiedValue, key] - ); + await this.conn.query(`UPDATE ${table} SET value = $1 WHERE id = $2`, [stringifiedValue, key]); } else { - await this.conn.query( - `INSERT INTO ${table} (id, value) VALUES ($1, $2)`, - [key, stringifiedValue] - ); + await this.conn.query(`INSERT INTO ${table} (id, value) VALUES ($1, $2)`, [key, stringifiedValue]); } return value; @@ -101,10 +83,7 @@ module.exports = class PostgresDriver { async deleteRowByKey(table, key) { this.checkConnection(); - const queryResult = await this.conn.query( - `DELETE FROM ${table} WHERE id = $1`, - [key] - ); + const queryResult = await this.conn.query(`DELETE FROM ${table} WHERE id = $1`, [key]); return queryResult.rowCount; } -}; \ No newline at end of file +};