diff --git a/commands/course.js b/commands/course.js index 07428e13..10ca6b79 100644 --- a/commands/course.js +++ b/commands/course.js @@ -127,9 +127,14 @@ module.exports = { ephemeral: true, }); } + // // Add it to the existing database to track. + /** @type {DBuser} */ + const userDB = global.userDB; + await userDB.add_user_role(interaction.user.id, role.name); // If they don't, let's add the role to them await interaction.member.roles.add(role); + return await interaction.reply({ content: `✅ | Added you to the chat for \`${course_with_alias}\`.`, ephemeral: true, @@ -190,6 +195,10 @@ module.exports = { in_overwrites(permissions, role.id) ) { // If they do remove the role + /** @type {DBuser} */ + const userDB = global.userDB; + userDB.remove_user_role(interaction.user.id, role.name); + await interaction.member.roles.remove(role); return await interaction.reply({ content: `✅ | Removed you from the role and chat for \`${course}\`.`, diff --git a/events/db_ready.js b/events/db_ready.js new file mode 100644 index 00000000..7104c8d4 --- /dev/null +++ b/events/db_ready.js @@ -0,0 +1,46 @@ +// @ts-check +const { DBuser } = require("../lib/database/database"); +const { CronJob } = require("cron"); + +const CSESOC_SERVER_ID = "693779865916276746"; +// const TEST_SERVER_ID = "1220297696829509713"; + +module.exports = { + name: "ready", + once: true, + async execute(client) { + /** @type {DBuser} */ + const userDB = new DBuser(); + global.userDB = userDB; + + // Set up an automatic database check to see if there is any out of date roles. + const role_job = new CronJob("0 0 12 * * *", async function () { + console.log("Performing daily check of old roles at 12:00pm"); + + const old_roles = await userDB.checkTimeAssigned(); + const guild = await client.guilds.fetch(CSESOC_SERVER_ID); + const roles = await guild.roles.fetch(); + + for (const removed_role of old_roles) { + try { + const member = await guild.members.fetch(removed_role.userid); + const role = roles.find((r) => r.name === removed_role.role_name); + + if (member && role) { + await member.roles.remove(role); + await userDB.remove_user_role(removed_role.userid, removed_role.role_name); + // console.log(`Removed role ${removed_role.role_name} from user ${removed_role.userid}`); + } else { + console.log( + `Could not find role ${removed_role.role_name} or user ${removed_role.userid}`, + ); + } + } catch (error) { + console.log(error); + } + } + }); + + role_job.start(); + }, +}; diff --git a/lib/database/database.js b/lib/database/database.js index 14b700c9..37f046c7 100644 --- a/lib/database/database.js +++ b/lib/database/database.js @@ -27,7 +27,7 @@ class DBuser { load_db_login() { // Get document, or throw exception on error try { - const doc = yaml.load(fs.readFileSync("../../config/database.yml")); + const doc = yaml.load(fs.readFileSync("./config/database.yml")); return doc; } catch (e) { console.log(e); @@ -90,14 +90,14 @@ class DBuser { const client = await this.pool.connect(); try { if ((await this.check_table("users")) == false) { - // console.log("Running create_table") + console.log("Running creating user table"); await client.query("BEGIN"); const query = `CREATE TABLE users ( userid INTEGER PRIMARY KEY, joindate DATE NOT NULL, leavedate DATE, userleft BOOLEAN - );`; + )`; await client.query(query); await client.query("COMMIT"); } @@ -114,21 +114,19 @@ class DBuser { async create_table_user_roles() { const client = await this.pool.connect(); try { - if ((await this.check_table("user_roles")) == false) { - // console.log("Running create_table") - await client.query("BEGIN"); - const query = `CREATE TABLE user_roles ( - rid INTEGER PRIMARY KEY, - userid INTEGER NOT NULL, - role varchar(64), - FOREIGN KEY (userid) - REFERENCES users (userid) - );`; - await client.query(query); - await client.query("COMMIT"); - } + // if ((await this.check_table("user_roles")) == false) { + + await client.query("BEGIN"); + const query = `CREATE TABLE user_roles ( + rid INTEGER PRIMARY KEY, + userid BIGINT NOT NULL, + role varchar(64), + time_assigned TIMESTAMP + )`; + await client.query(query); + await client.query("COMMIT"); } catch (ex) { - console.log(`Something wrong happend ${ex}`); + console.log(`Something wrong happende:${ex}`); } finally { await client.query("ROLLBACK"); client.release(); @@ -170,12 +168,12 @@ class DBuser { // console.log("Running create_table") await client.query("BEGIN"); const query = `CREATE TABLE user_permissions ( - pid INTEGER PRIMARY KEY, - userid INTEGER NOT NULL, - permission varchar(64), - FOREIGN KEY (userid) - REFERENCES users (userid) - );`; + pid INTEGER PRIMARY KEY, + userid INTEGER NOT NULL, + permission varchar(64), + FOREIGN KEY (userid) + REFERENCES users (userid) + );`; await client.query(query); await client.query("COMMIT"); } @@ -250,18 +248,18 @@ class DBuser { const client = await this.pool.connect(); try { await client.query("BEGIN"); - let query = "SELECT max(rid) from user_roles"; let result = await client.query(query); const count = result.rows[0]["max"] + 1; - query = "INSERT INTO user_roles (RID, USERID, ROLE) VALUES ($1,$2,$3)"; + query = + "INSERT INTO user_roles (RID, USERID, ROLE, TIME_ASSIGNED) VALUES ($1,$2,$3,NOW())"; const values = [count, userid, role]; result = await client.query(query, values); await client.query("COMMIT"); } catch (ex) { - console.log(`Something wrong happend ${ex}`); + console.log(`Something wrong happend when trying to add user role to table ${ex}`); } finally { await client.query("ROLLBACK"); client.release(); @@ -275,8 +273,7 @@ class DBuser { try { await client.query("BEGIN"); const values = [userid, role]; - const query = `DELETE FROM user_roles - where userid = $1 and role = $2`; + const query = `DELETE FROM user_roles where userid = $1 and role = $2`; await client.query(query, values); await client.query("COMMIT"); } catch (ex) { @@ -456,6 +453,30 @@ class DBuser { // console.log("Client released successfully.") } } + + async checkTimeAssigned() { + const client = await this.pool.connect(); + try { + // Query to select rows where time_assigned is older than 1 hour + const query = ` + SELECT * FROM user_roles WHERE time_assigned < NOW() - interval '1 year' + `; + + const result = await client.query(query); + + const old_roles = result.rows.map((row) => ({ + role_name: row.role, + userid: row.userid, + })); + + return old_roles; + } catch (error) { + console.error(error); + return []; + } finally { + client.release(); + } + } } module.exports = { diff --git a/package-lock.json b/package-lock.json index 870c269a..3a634130 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "chartjs-node-canvas": "^4.1.6", "cheerio": "^1.0.0-rc.12", "closest-match": "1.3.3", + "cron": "^3.1.7", "csv-parser": "3.0.0", "csv-writer": "1.6.0", "discord-api-types": "0.37.90", @@ -26,7 +27,7 @@ "dotenv": "16.4.5", "js-yaml": "4.1.0", "mathjs": "^13.0.0", - "node-cron": "^3.0.2", + "node-cron": "^3.0.3", "nodemailer": "6.9.13", "nodemon": "^3.0.0", "pg": "8.12.0", @@ -875,6 +876,11 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" + }, "node_modules/@types/node": { "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", @@ -1555,6 +1561,15 @@ } } }, + "node_modules/cron": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.7.tgz", + "integrity": "sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw==", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.4.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3169,6 +3184,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-bytes.js": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", @@ -5934,6 +5957,11 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, + "@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" + }, "@types/node": { "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", @@ -6425,6 +6453,15 @@ "parse-json": "^5.2.0" } }, + "cron": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.7.tgz", + "integrity": "sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw==", + "requires": { + "@types/luxon": "~3.4.0", + "luxon": "~3.4.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7609,6 +7646,11 @@ "yallist": "^3.0.2" } }, + "luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==" + }, "magic-bytes.js": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", diff --git a/package.json b/package.json index ead7aa0d..4772d9c3 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "chartjs-node-canvas": "^4.1.6", "cheerio": "^1.0.0-rc.12", "closest-match": "1.3.3", + "cron": "^3.1.7", "csv-parser": "3.0.0", "csv-writer": "1.6.0", "discord-api-types": "0.37.90", @@ -41,7 +42,7 @@ "dotenv": "16.4.5", "js-yaml": "4.1.0", "mathjs": "^13.0.0", - "node-cron": "^3.0.2", + "node-cron": "^3.0.3", "nodemailer": "6.9.13", "nodemon": "^3.0.0", "pg": "8.12.0",