From ccf2372cba647e42a2b4bf577a72b681bb150b30 Mon Sep 17 00:00:00 2001 From: Wolfdragon24 Date: Thu, 1 Aug 2024 17:41:59 +1000 Subject: [PATCH 1/2] Implementation of user join and leave logging and admin command to facilitate replacing old db table --- commands/admin.js | 85 +++---------------------------------- events/guildMemberAdd.js | 23 ++++++++++ events/guildMemberRemove.js | 15 +++++++ lib/database/database.js | 25 ++++++++++- 4 files changed, 68 insertions(+), 80 deletions(-) create mode 100644 events/guildMemberAdd.js create mode 100644 events/guildMemberRemove.js diff --git a/commands/admin.js b/commands/admin.js index cbb5513e..bea9f98f 100644 --- a/commands/admin.js +++ b/commands/admin.js @@ -2,28 +2,7 @@ const { SlashCommandBuilder } = require("@discordjs/builders"); const { Permissions } = require("discord.js"); const COMMAND_KICKUNVERIFIED = "kickunverified"; -const COMMAND_MIGRATE = "migratecourses"; -const COMMAND_REMOVECOURSEROLES = "nukeremovecourseroles"; - -// yeah i know this code is copy pasted from the other file -// but whatever, the migration command is temporary! -const is_valid_course = (course) => { - const reg_comp_course = /^comp\d{4}$/; - const reg_math_course = /^math\d{4}$/; - const reg_binf_course = /^binf\d{4}$/; - const reg_engg_course = /^engg\d{4}$/; - const reg_seng_course = /^seng\d{4}$/; - const reg_desn_course = /^desn\d{4}$/; - - return ( - reg_comp_course.test(course.toLowerCase()) || - reg_math_course.test(course.toLowerCase()) || - reg_binf_course.test(course.toLowerCase()) || - reg_engg_course.test(course.toLowerCase()) || - reg_seng_course.test(course.toLowerCase()) || - reg_desn_course.test(course.toLowerCase()) - ); -}; +const COMMAND_DROPUSERTABLE = "dropusertable"; module.exports = { data: new SlashCommandBuilder() @@ -36,19 +15,8 @@ module.exports = { ) .addSubcommand((subcommand) => subcommand - .setName(COMMAND_MIGRATE) - .setDescription("Migrates a course role to permission overwrites.") - .addStringOption((option) => - option - .setName("course") - .setDescription("Course role to remove") - .setRequired(true), - ), - ) - .addSubcommand((subcommand) => - subcommand - .setName(COMMAND_REMOVECOURSEROLES) - .setDescription("WARNING: Removes course roles from the server."), + .setName(COMMAND_DROPUSERTABLE) + .setDescription("Deletes the user table and reliant tables."), ), async execute(interaction) { try { @@ -93,50 +61,11 @@ module.exports = { }); }); return await interaction.reply("Removed unverified members."); - } else if (interaction.options.getSubcommand() === COMMAND_MIGRATE) { - const course = interaction.options.getString("course"); - if (!is_valid_course(course)) { - return await interaction.reply("Error: invalid course."); - } - - const role = await interaction.guild.roles.cache.find( - (course_role) => course_role.name.toLowerCase() === course.toLowerCase(), - ); - - if (role === undefined) { - return await interaction.reply("Error: no role exists for course " + course); - } - - const channel = await interaction.guild.channels.cache.find( - (role_channel) => role_channel.name.toLowerCase() === role.name.toLowerCase(), - ); - - if (channel === undefined) { - return await interaction.reply("Error: no channel exists for course " + course); - } - - await interaction.deferReply(); - for (const member of role.members.values()) { - await channel.permissionOverwrites.create(member, { - VIEW_CHANNEL: true, - }); - } - return await interaction.editReply( - "Migrated course role to permission overwrites.", - ); - } else if (interaction.options.getSubcommand() === COMMAND_REMOVECOURSEROLES) { - // get all roles, and find courses which match the regex - const course_roles = await interaction.guild.roles.cache.filter((role) => - is_valid_course(role.name), - ); - - await interaction.deferReply(); - - for (const role of course_roles.values()) { - await role.delete(); - } + } else if (interaction.options.getSubcommand() === COMMAND_DROPUSERTABLE) { + const userDB = global.userDB; + await userDB.drop_table(); - return await interaction.editReply("Removed all course roles."); + return await interaction.editReply("Deleted user table."); } return await interaction.reply("Error: unknown subcommand."); diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js new file mode 100644 index 00000000..18325fe5 --- /dev/null +++ b/events/guildMemberAdd.js @@ -0,0 +1,23 @@ +const { Events } = require("discord.js"); + +const CSESOC_SERVER_ID = "693779865916276746"; +const REPORT_CHANNEL_ID = "1225243371036082187"; + +module.exports = { + name: Events.GuildMemberAdd, + once: false, + execute(member) { + /** @type {DBuser} */ + const userDB = global.userDB; + + // Get report channel + if (member.user.bot || member.user.system || member.guild.id !== CSESOC_SERVER_ID) return; + + userDB.user_join(member.id).then((joinType) => { + if (joinType === "rejoin") { + const reportChannel = member.guild.channels.cache.get(REPORT_CHANNEL_ID); + reportChannel.send(`${member.user} (${member.user.tag}) has rejoined the server.`); + } + }); + }, +}; diff --git a/events/guildMemberRemove.js b/events/guildMemberRemove.js new file mode 100644 index 00000000..d4376905 --- /dev/null +++ b/events/guildMemberRemove.js @@ -0,0 +1,15 @@ +const { Events } = require("discord.js"); + +module.exports = { + name: Events.GuildMemberRemove, + once: false, + execute(member) { + /** @type {DBuser} */ + const userDB = global.userDB; + + // Get report channel + if (member.user.bot || member.user.system) return; + + userDB.user_leave(member.id); + }, +}; diff --git a/lib/database/database.js b/lib/database/database.js index 37f046c7..426cac20 100644 --- a/lib/database/database.js +++ b/lib/database/database.js @@ -93,7 +93,7 @@ class DBuser { console.log("Running creating user table"); await client.query("BEGIN"); const query = `CREATE TABLE users ( - userid INTEGER PRIMARY KEY, + userid TEXT PRIMARY KEY, joindate DATE NOT NULL, leavedate DATE, userleft BOOLEAN @@ -196,7 +196,7 @@ class DBuser { try { await client.query("BEGIN"); const values = [time, true, userid]; - const query = "UPDATE users SET leavedate = $1 , userleft = $2 where userid = $3"; + const query = "UPDATE users SET leavedate = $1 , userleft = $2 where userid = $3"; await client.query(query, values); await client.query("COMMIT"); @@ -215,6 +215,8 @@ class DBuser { time.setMilliseconds(0); time = time.toISOString(); + let type = "new"; + const client = await this.pool.connect(); try { await client.query("BEGIN"); @@ -227,6 +229,9 @@ class DBuser { query = "UPDATE users SET joindate=$1, userleft=$2 where userid=$3"; values = [time, false, userid]; await client.query(query, values); + + // Report rejoining user to channel + type = "rejoin"; } else { query = "INSERT INTO users (USERID, JOINDATE, LEAVEDATE, USERLEFT) VALUES ($1,$2,$3,$4)"; @@ -241,6 +246,8 @@ class DBuser { client.release(); // console.log("Client released successfully.") } + + return type; } // Adding a user role @@ -477,6 +484,20 @@ class DBuser { client.release(); } } + + async deleteUsers() { + // Temporary - delete after use + const client = await this.pool.connect(); + try { + // Query to delete users who have left the server + const query = `DROP TABLE users CASCADE;`; + await client.query(query); + } catch (error) { + console.error(error); + } finally { + client.release(); + } + } } module.exports = { From 2c9ed2b4f43076e24ef89308db07615944f53a4c Mon Sep 17 00:00:00 2001 From: Wolfdragon24 Date: Tue, 6 Aug 2024 22:38:54 +1000 Subject: [PATCH 2/2] Complete implementation of rejoin notices --- events/guildMemberAdd.js | 23 +++++++++++++++++------ lib/database/database.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/events/guildMemberAdd.js b/events/guildMemberAdd.js index 18325fe5..b690bdea 100644 --- a/events/guildMemberAdd.js +++ b/events/guildMemberAdd.js @@ -1,7 +1,7 @@ const { Events } = require("discord.js"); const CSESOC_SERVER_ID = "693779865916276746"; -const REPORT_CHANNEL_ID = "1225243371036082187"; +const REPORT_CHANNEL_ID = "1270283342176059443"; module.exports = { name: Events.GuildMemberAdd, @@ -13,11 +13,22 @@ module.exports = { // Get report channel if (member.user.bot || member.user.system || member.guild.id !== CSESOC_SERVER_ID) return; - userDB.user_join(member.id).then((joinType) => { - if (joinType === "rejoin") { - const reportChannel = member.guild.channels.cache.get(REPORT_CHANNEL_ID); - reportChannel.send(`${member.user} (${member.user.tag}) has rejoined the server.`); - } + // Get old user info before joining + userDB.get_user_info(member.id).then((user_data) => { + userDB.user_join(member.id).then((joinType) => { + if (joinType === "rejoin") { + // Fetch the channel to output details + const reportChannel = member.guild.channels.cache.get(REPORT_CHANNEL_ID); + + // Fetch formatted date values from joining and leaving events + const joinDate = user_data.joinDate.toLocaleDateString("en-AU"); + const leaveDate = user_data.leaveDate.toLocaleDateString("en-AU"); + + reportChannel.send( + `${member.user} (${member.user.tag}) has rejoined the server. [Last in server: ${leaveDate}, Last joined: ${joinDate}]`, + ); + } + }); }); }, }; diff --git a/lib/database/database.js b/lib/database/database.js index 426cac20..323e9440 100644 --- a/lib/database/database.js +++ b/lib/database/database.js @@ -250,6 +250,38 @@ class DBuser { return type; } + async get_user_info(userid) { + const client = await this.pool.connect(); + let info = null; + + try { + await client.query("BEGIN"); + + const query = "select * from users where userid = $1"; + const values = [userid]; + const result = await client.query(query, values); + + if (result.rows.length != 0) { + const row = result.rows[0]; + + info = { + userId: row["userid"], + joinDate: row["joindate"], + leaveDate: row["leavedate"], + userLeft: row["userleft"], + }; + } + } catch (ex) { + console.log(`An unexpected error occurred: ${ex}`); + } finally { + await client.query("ROLLBACK"); + client.release(); + // console.log("Client released successfully.") + } + + return info; + } + // Adding a user role async add_user_role(userid, role) { const client = await this.pool.connect();