From 054128a9a08e81adda471180b574213dba99baba Mon Sep 17 00:00:00 2001 From: imagine-hussain <93496985+imagine-hussain@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:14:04 +1100 Subject: [PATCH 01/14] migrate from insou api to circles for the handbook commands (#128) * migrate from insou api to circles for the handbook commands * remove trailing forward slash in handbook.json * handbook: the handbook is fixed * Update handbook.js commented out a console.log --------- Co-authored-by: tunein Co-authored-by: zcDay1 <113964162+zcDay1@users.noreply.github.com> --- commands/handbook.js | 52 +++++++++++++++++++++++--------------------- config/database.yml | 2 +- config/handbook.json | 2 +- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/commands/handbook.js b/commands/handbook.js index 6be75e35..339fd563 100644 --- a/commands/handbook.js +++ b/commands/handbook.js @@ -27,8 +27,11 @@ module.exports = { let data; try { - const response = await axios.get(`${apiURL}${courseCode}`); + // Documented at: + // https://circlesapi.csesoc.app/docs#/courses/get_course_courses_getCourse__courseCode__get + const response = await axios.get(`${apiURL}/courses/getCourse/${courseCode}`); data = response.data; + // console.log(data); } catch (e) { return await interaction.reply({ content: "Invalid course code.", @@ -36,27 +39,32 @@ module.exports = { }); } + const { + title, code, UOC, level, description, study_level, school, campus, + equivalents, raw_requirements, exclusions, handbook_note, terms + } = data; + const courseInfo = new MessageEmbed() - .setTitle(data["title"]) - .setURL(`${handbookURL}${courseCode}`) + .setTitle(title) + .setURL(`${handbookURL}/${code}`) .setColor(0x3a76f8) .setAuthor( - `Course Info: ${courseCode} (${data["credit_points"]} UOC)`, + `Course Info: ${code} (${UOC} UOC)`, "https://i.imgur.com/EE3Q40V.png", ) .addFields( { name: "Overview", - value: textVersion(data["description"]).substring( + value: textVersion(description).substring( 0, - Math.min(textVersion(data["description"]).indexOf("\n"), 1024), + Math.min(textVersion(description).indexOf("\n"), 1024), ), inline: false, }, { name: "Enrolment Requirements", value: - data["enrolment_requirements"].replace( + raw_requirements.replace( /[A-Z]{4}[0-9]{4}/g, `[$&](${handbookURL}$&)`, ) || "None", @@ -64,39 +72,33 @@ module.exports = { }, { name: "Offering Terms", - value: data["offering_terms"], - inline: true, - }, - { - name: "Delivery Mode", - value: data["delivery_mode"], + value: terms.join(", "), inline: true, }, { name: "Equivalent Courses", - value: - data["equivalent_courses"] - .map((course) => `[${course}](${handbookURL}${course})`) - .join(", ") || "None", + value: + Object.keys(equivalents) + .map((course) => `[${course}](${course})`) + .join(", ") || "None", inline: true, }, { name: "Exclusion Courses", value: - data["exclusion_courses"] + Object.keys(exclusions) .map((course) => `[${course}](${handbookURL}${course})`) .join(", ") || "None", inline: true, }, - { - name: "Course Outline", - value: `[${courseCode} Course Outline](${data["course_outline_url"]})`, - inline: true, - }, + /* { */ + /* name: "Course Outline", */ + /* value: `[${courseCode} Course Outline](${data["course_outline_url"]})`, */ + /* inline: true, */ + /* }, */ ) .setTimestamp() - .setFooter("Data fetched from Zac's Handbook API"); - + .setFooter("Data fetched from Circles' Api"); await interaction.reply({ embeds: [courseInfo] }); } }, diff --git a/config/database.yml b/config/database.yml index 1fa06e4f..eac5e48b 100644 --- a/config/database.yml +++ b/config/database.yml @@ -6,4 +6,4 @@ user: user dbname: bot password: pass host: 0.0.0.0 -port: 40041 +port: 40041 \ No newline at end of file diff --git a/config/handbook.json b/config/handbook.json index 3fe12ea2..9a8d7097 100644 --- a/config/handbook.json +++ b/config/handbook.json @@ -1,4 +1,4 @@ { - "apiURL": "https://handbook.insou.dev/api/v1/course/", + "apiURL": "https://circlesapi.csesoc.app", "handbookURL": "https://www.handbook.unsw.edu.au/undergraduate/courses/2022/" } From ea97f3371f9d40e2fd5f69b2d5a434a6a2de0c08 Mon Sep 17 00:00:00 2001 From: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:25:08 +1100 Subject: [PATCH 02/14] prettier formatted --- commands/handbook.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/commands/handbook.js b/commands/handbook.js index 339fd563..64bf8b60 100644 --- a/commands/handbook.js +++ b/commands/handbook.js @@ -40,18 +40,26 @@ module.exports = { } const { - title, code, UOC, level, description, study_level, school, campus, - equivalents, raw_requirements, exclusions, handbook_note, terms + title, + code, + UOC, + level, + description, + study_level, + school, + campus, + equivalents, + raw_requirements, + exclusions, + handbook_note, + terms, } = data; const courseInfo = new MessageEmbed() .setTitle(title) .setURL(`${handbookURL}/${code}`) .setColor(0x3a76f8) - .setAuthor( - `Course Info: ${code} (${UOC} UOC)`, - "https://i.imgur.com/EE3Q40V.png", - ) + .setAuthor(`Course Info: ${code} (${UOC} UOC)`, "https://i.imgur.com/EE3Q40V.png") .addFields( { name: "Overview", @@ -77,16 +85,16 @@ module.exports = { }, { name: "Equivalent Courses", - value: + value: Object.keys(equivalents) - .map((course) => `[${course}](${course})`) - .join(", ") || "None", + .map((course) => `[${course}](${course})`) + .join(", ") || "None", inline: true, }, { name: "Exclusion Courses", value: - Object.keys(exclusions) + Object.keys(exclusions) .map((course) => `[${course}](${handbookURL}${course})`) .join(", ") || "None", inline: true, From 81e5085ff75b2f40d03d5fc0bf4f2a1eb31001f8 Mon Sep 17 00:00:00 2001 From: Jared L <48422312+lhjt@users.noreply.github.com> Date: Wed, 22 Mar 2023 17:57:21 +1100 Subject: [PATCH 03/14] chore(deps): update `renovate` config --- renovate.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/renovate.json b/renovate.json index 28505fd0..7262f36f 100644 --- a/renovate.json +++ b/renovate.json @@ -9,16 +9,23 @@ "automerge": false, "automergeType": "branch" }, + { + "matchUpdateTypes": ["patch"], + "groupName": "weekly patch updates", + "schedule": ["before 5am every monday"], + "addLabels": ["deps: patches"] + }, + { + "matchUpdateTypes": ["minor"], + "groupName": "weekly minor updates", + "schedule": ["before 5am every monday"], + "addLabels": ["deps: minor"] + }, { "groupName": "docker-github-actions", "matchPackagePatterns": ["docker/*"], "automerge": true, "automergeType": "branch" }, - { - "matchUpdateTypes": ["patch"], - "groupName": "weekly patch updates", - "schedule": ["before 5am every monday"] - } ] } From 883a0d866927d12fc3239bae0445879cba0aa054 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:46:05 +1100 Subject: [PATCH 04/14] chore(deps): update docker/build-push-action action to v4 (#134) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 26102c42..b8316d72 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -39,7 +39,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GH_TOKEN }} - name: Build and push Docker image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: . push: ${{ github.event_name != 'pull_request' && ( github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/projects-bot' ) }} From d0656cabc2515ba37f59cb982d15994b1c874fe6 Mon Sep 17 00:00:00 2001 From: Alexander C <113964162+zcDay1@users.noreply.github.com> Date: Tue, 16 May 2023 13:30:04 +1000 Subject: [PATCH 05/14] Projects description command feature (#145) * projects-descriptions v1 commit * project-descriptions command v2 commit * linting fixes for project-descriptions.js, and for handbook.js --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> --- commands/handbook.js | 10 ++-- commands/project-descriptions.js | 85 ++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 commands/project-descriptions.js diff --git a/commands/handbook.js b/commands/handbook.js index 64bf8b60..3799f703 100644 --- a/commands/handbook.js +++ b/commands/handbook.js @@ -43,15 +43,15 @@ module.exports = { title, code, UOC, - level, + // level, description, - study_level, - school, - campus, + // study_level, + // school, + // campus, equivalents, raw_requirements, exclusions, - handbook_note, + // handbook_note, terms, } = data; diff --git a/commands/project-descriptions.js b/commands/project-descriptions.js new file mode 100644 index 00000000..0b186b0d --- /dev/null +++ b/commands/project-descriptions.js @@ -0,0 +1,85 @@ +const { SlashCommandBuilder } = require("@discordjs/builders"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("project-descriptions") + .setDescription("Returns a description for each project under CSESoc Development!") + .addStringOption((option) => + option + .setName("project") + .setDescription("Which project do you want to be introduced to?") + .setRequired(true) + .addChoices([ + ["Chaos", "chaos"], + ["Circles", "circles"], + ["CS Electives", "cselectives"], + ["Discord Bot", "discordbot"], + ["Freerooms", "freerooms"], + ["Jobsboard", "jobsboard"], + ["Notangles", "notangles"], + ["Structs.sh", "structs.sh"], + ["UI/UX", "ui/ux"], + ["Website", "website"], + ]), + ), + + async execute(interaction) { + const parsedOption = interaction.options.get("project").value.toLowerCase(); + // console.log(`.${parsedOption}.`); + switch (parsedOption) { + case "chaos": + await interaction.reply( + "Chaos is a CSESoc internal recruitment tool written in Rust.", + ); + break; + case "circles": + await interaction.reply( + "Circles is a degree planner that helps you choose courses, plan out your terms and check progression.", + ); + break; + case "cselectives": + await interaction.reply( + "Unsure about what a course is like? Worry no more; CSElectives lets you read and write reviews of UNSW CSE courses.", + ); + break; + case "discordbot": + await interaction.reply( + "CSESoc Discord Bot is your friendly helper in all things fun and CSE.", + ); + break; + case "freerooms": + await interaction.reply( + "Looking for a room to study in? Freerooms lets you see which on-campus rooms are vacant and which ones are booked.", + ); + break; + case "jobsboard": + await interaction.reply( + "Jobsboard is an app that connects CSE students with companies looking for recruits.", + ); + break; + case "notangles": + await interaction.reply( + "Notangles is a timetable planning app for UNSW students to build their perfect timetable, even before class registration opens.", + ); + break; + case "structs.sh": + await interaction.reply("Structs.sh is an interactive algorithm visualiser."); + break; + case "ui/ux": + await interaction.reply( + "The CSESoc Development UI/UX team works with all things related to user interface and experience design!", + ); + break; + case "website": + await interaction.reply( + "The website team are in charge of writing the software for the CSESoc website.", + ); + break; + default: + await interaction.reply( + "Error: the switch case has fallen through to the default case.", + ); + break; + } + }, +}; From 9a4c9ec5cd190caab990b97ae457f88ff7f201fa Mon Sep 17 00:00:00 2001 From: abiramen <7523422+abiramen@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:50:18 +1000 Subject: [PATCH 06/14] disable annoying carrotboard pins --- config/carrotboard.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/carrotboard.yaml b/config/carrotboard.yaml index 32c98a8b..7a4a11b6 100644 --- a/config/carrotboard.yaml +++ b/config/carrotboard.yaml @@ -3,5 +3,5 @@ leaderboard_channel_id: 979516523335016509 carrotboard_alert_channel_id: 894267846106963998 guild_id: 884747109935497236 carrot_emoji: πŸ₯³ -minimum_carrot_count: 1 -minimum_pin_count: 1 +minimum_carrot_count: 999999 +minimum_pin_count: 999999999999 From 2f59c71119d3fbeed31bed148a6e46a8c7f21aa2 Mon Sep 17 00:00:00 2001 From: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> Date: Sat, 16 Sep 2023 10:50:25 +1000 Subject: [PATCH 07/14] rolesPermOverride command is a script or one-time use command to attach every course role with permissions to view and send messages in any text channel with identical name. Please review and test before merging --- commands/rolesPermOverride.js | 96 +++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 commands/rolesPermOverride.js diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js new file mode 100644 index 00000000..7f484a71 --- /dev/null +++ b/commands/rolesPermOverride.js @@ -0,0 +1,96 @@ +const { SlashCommandBuilder } = require("@discordjs/builders"); +const { Permissions } = require("discord.js"); + + + +// map of course aliases to their actual names +const course_aliases = { + comp6841: "comp6441", + comp9044: "comp2041", + comp3891: "comp3231", + comp9201: "comp3231", + comp9101: "comp3121", + comp9331: "comp3331", + comp9415: "comp3421", + comp9801: "comp3821", + comp9102: "comp3131", + comp9154: "comp3151", + comp9164: "comp3161", + comp9211: "comp3211", + comp9222: "comp3221", + comp9814: "comp3411", + comp9511: "comp3511", + comp9900: "comp3900", + seng4920: "comp4920", + comp9337: "comp4337", + math1141: "math1131", + math1241: "math1231", +}; + +const get_real_course_name = (course) => { + if (course_aliases[course.toLowerCase()]) { + return course_aliases[course.toLowerCase()]; + } + return course.toLowerCase(); +}; + + + + +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()) + ); +}; + +module.exports = { + data: new SlashCommandBuilder() + .setName("rolespermoverride") + .setDescription("Looks for matches between roles and course chats and attaches permissions."), + async execute(interaction) { + try { + // for all roles with name == chat name involving 4 letter prefix comp, seng, engg or binf, + // give the role the permission override to participate in the matching channel. + message.guild.roles.forEach(role => { + if (is_valid_course(role)) { + console.log("hi"); + message.guild.channels.forEach(channel => { + if (channel.type === "GUILD_TEXT" && channel.name.toLowerCase() === role.toLowerCase()) { + // Remove all permissions from a role + role.setPermissions(0n) + .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`)) + .catch(console.error); + // Set the permissions of the role + // Add the member to the channel's permission overwrites + channel.permissionOverwrites.create(role, { + VIEW_CHANNEL: true, + SEND_MESSAGES: true, + }); + interaction.reply({ + content: `βœ… | removed all permissions and set new permission overwrites for + ${interaction.channel.name} and ${role.name}.`, + ephemeral: true, + }); + } + }); + } + }); + + + } catch (error) { + await interaction.reply("Error: " + error); + } + }, +}; \ No newline at end of file From 617c37359881dcdc6792014c02f7bfd82420ce97 Mon Sep 17 00:00:00 2001 From: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> Date: Sat, 16 Sep 2023 10:53:36 +1000 Subject: [PATCH 08/14] Revert "rolesPermOverride command is a script or one-time use command to attach every course role with permissions to view and send messages in any text channel with identical name. Please review and test before merging" This reverts commit 2f59c71119d3fbeed31bed148a6e46a8c7f21aa2. --- commands/rolesPermOverride.js | 96 ----------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 commands/rolesPermOverride.js diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js deleted file mode 100644 index 7f484a71..00000000 --- a/commands/rolesPermOverride.js +++ /dev/null @@ -1,96 +0,0 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); -const { Permissions } = require("discord.js"); - - - -// map of course aliases to their actual names -const course_aliases = { - comp6841: "comp6441", - comp9044: "comp2041", - comp3891: "comp3231", - comp9201: "comp3231", - comp9101: "comp3121", - comp9331: "comp3331", - comp9415: "comp3421", - comp9801: "comp3821", - comp9102: "comp3131", - comp9154: "comp3151", - comp9164: "comp3161", - comp9211: "comp3211", - comp9222: "comp3221", - comp9814: "comp3411", - comp9511: "comp3511", - comp9900: "comp3900", - seng4920: "comp4920", - comp9337: "comp4337", - math1141: "math1131", - math1241: "math1231", -}; - -const get_real_course_name = (course) => { - if (course_aliases[course.toLowerCase()]) { - return course_aliases[course.toLowerCase()]; - } - return course.toLowerCase(); -}; - - - - -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()) - ); -}; - -module.exports = { - data: new SlashCommandBuilder() - .setName("rolespermoverride") - .setDescription("Looks for matches between roles and course chats and attaches permissions."), - async execute(interaction) { - try { - // for all roles with name == chat name involving 4 letter prefix comp, seng, engg or binf, - // give the role the permission override to participate in the matching channel. - message.guild.roles.forEach(role => { - if (is_valid_course(role)) { - console.log("hi"); - message.guild.channels.forEach(channel => { - if (channel.type === "GUILD_TEXT" && channel.name.toLowerCase() === role.toLowerCase()) { - // Remove all permissions from a role - role.setPermissions(0n) - .then(updated => console.log(`Updated permissions to ${updated.permissions.bitfield}`)) - .catch(console.error); - // Set the permissions of the role - // Add the member to the channel's permission overwrites - channel.permissionOverwrites.create(role, { - VIEW_CHANNEL: true, - SEND_MESSAGES: true, - }); - interaction.reply({ - content: `βœ… | removed all permissions and set new permission overwrites for - ${interaction.channel.name} and ${role.name}.`, - ephemeral: true, - }); - } - }); - } - }); - - - } catch (error) { - await interaction.reply("Error: " + error); - } - }, -}; \ No newline at end of file From da9b795c9ebbc890e3b25f60328b4b0f92f3ed9f Mon Sep 17 00:00:00 2001 From: Alexander C <113964162+zcDay1@users.noreply.github.com> Date: Wed, 20 Sep 2023 14:51:25 +1000 Subject: [PATCH 09/14] rolesPermOverride command (#152) * rolesPermOverride command is a script or one-time use command to attach every course role with permissions to view and send messages in any text channel with identical name. Please review and test before mergin * BUGGY CODE but still progress over the previous commit, I will come back this evening to fix * rolesPermOverride command: now admin-only command, and I've tested it for various cases regarding if a channel exists, if a role exists for a channel that doesnt exist, if there are two channels with the same name as a role and vice versa, and it works without error --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> --- commands/rolesPermOverride.js | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 commands/rolesPermOverride.js diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js new file mode 100644 index 00000000..21e05e75 --- /dev/null +++ b/commands/rolesPermOverride.js @@ -0,0 +1,93 @@ +const { SlashCommandBuilder } = require("@discordjs/builders"); +const { Permissions } = require("discord.js"); + +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()) + ); +}; + +function editChannels(interaction, channels, role) { + channels.forEach((channel) => { + if ( + channel.type === "GUILD_TEXT" && + channel.name.toLowerCase() === role.name.toLowerCase() + ) { + // Remove all permissions from a role + role.setPermissions(0n) + .then((updated) => + console.log(`Updated permissions to ${updated.permissions.bitfield}`), + ) + .catch(console.error); + // Set the permissions of the role + // Add the member to the channel's permission overwrites + channel.permissionOverwrites.create(role, { + VIEW_CHANNEL: true, + SEND_MESSAGES: true, + }); + console.log(channel.name, role.name); + } + }); +} + +function editRoles(interaction, roles) { + roles.forEach((role) => { + if (is_valid_course(role.name)) { + interaction.guild.channels + .fetch() + .then( + (channels) => ( + editChannels(interaction, channels, role), + console.log(`There are ${channels.size} channels.`) + ), + ) + .catch(console.error); + } + }); + interaction.reply({ + content: `βœ… | found course chats and matching roles, cleared and set permission overwrites for roles.`, + ephemeral: true, + }); +} + +module.exports = { + data: new SlashCommandBuilder() + .setName("rolespermoverride") + .setDescription( + "Looks for matches between roles and course chats and attaches permissions.", + ), + async execute(interaction) { + try { + if (!interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) { + return await interaction.reply({ + content: "You do not have permission to execute this command.", + ephemeral: true, + }); + } + // for all roles with name == chat name involving 4 letter prefix comp, seng, engg or binf, + + // give the role the permission override to participate in the matching channel. + interaction.guild.roles + .fetch() + .then( + (roles) => ( + editRoles(interaction, roles), console.log(`There are ${roles.size} roles.`) + ), + ) + .catch(console.error); + } catch (error) { + await interaction.reply("Error: " + error); + } + }, +}; From d55e0930568d5657f7f6ce3f1de2e1d57beaa30d Mon Sep 17 00:00:00 2001 From: Alexander C <113964162+zcDay1@users.noreply.github.com> Date: Sun, 24 Sep 2023 23:31:03 +1000 Subject: [PATCH 10/14] Roles perm override (#153) * rolesPermOverride command is a script or one-time use command to attach every course role with permissions to view and send messages in any text channel with identical name. Please review and test before mergin * BUGGY CODE but still progress over the previous commit, I will come back this evening to fix * rolesPermOverride command: now admin-only command, and I've tested it for various cases regarding if a channel exists, if a role exists for a channel that doesnt exist, if there are two channels with the same name as a role and vice versa, and it works without error * course.js: removed code to create individual user permission overwrites in each channel --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> --- commands/course.js | 96 ++-------------------------------------------- 1 file changed, 4 insertions(+), 92 deletions(-) diff --git a/commands/course.js b/commands/course.js index 15d87caa..b55544c1 100644 --- a/commands/course.js +++ b/commands/course.js @@ -128,52 +128,8 @@ module.exports = { }); } - // Otherwise, find a channel with the same name as the course - const channel = await interaction.guild.channels.cache.find( - (c) => c.name.toLowerCase() === course.toLowerCase(), - ); - - // Make sure that the channel exists, and is a text channel - if (channel === undefined) { - return await interaction.reply({ - content: `❌ | The course chat for \`${course}\` does not exist. If you'd like for it to be created, please raise a ticket in <#${MODERATION_REQUEST_CHANNEL}>.`, - ephemeral: true, - }); - } else if (channel.type !== "GUILD_TEXT") { - return await interaction.reply({ - content: `❌ | The course chat for \`${course}\` is not a text channel.`, - ephemeral: true, - }); - } - - const permissions = new Permissions( - channel.permissionsFor(interaction.user.id).bitfield, - ); - - // Check if the member already has an entry in the channel's permission overwrites, and update - // the entry if they do just to make sure that they have the correct permissions - if ( - permissions.has([ - Permissions.FLAGS.VIEW_CHANNEL, - Permissions.FLAGS.SEND_MESSAGES, - ]) - ) { - await channel.permissionOverwrites.edit(interaction.member, { - VIEW_CHANNEL: true, - }); - return await interaction.reply({ - content: `❌ | You are already in the course chat for \`${course_with_alias}\`.`, - ephemeral: true, - }); - } - - // Add the member to the channel's permission overwrites - await channel.permissionOverwrites.create(interaction.member, { - VIEW_CHANNEL: true, - }); - return await interaction.reply({ - content: `βœ… | Added you to the chat for ${course_with_alias}.`, + content: `βœ… | End of command - ${course_with_alias}.`, ephemeral: true, }); } else if (interaction.options.getSubcommand() === COMMAND_LEAVE) { @@ -182,7 +138,7 @@ module.exports = { if (!is_valid_course(course)) { return await interaction.reply({ - content: `❌ | You are not allowed to leave this channel using this command.`, + content: `❌ | Not a valid course.`, ephemeral: true, }); } @@ -196,7 +152,7 @@ module.exports = { if (role !== undefined) { if (!interaction.member.roles.cache.has(role.id)) { return await interaction.reply({ - content: `❌ | You are not in the course chat for \`${course}\`.`, + content: `❌ | You do not have the role for \`${course}\`.`, ephemeral: true, }); } @@ -204,55 +160,11 @@ module.exports = { // If they do, let's remove the role from them await interaction.member.roles.remove(role); return await interaction.reply({ - content: `βœ… | Removed you from the chat for \`${course}\`.`, + content: `βœ… | Removed you from the role and chat for \`${course}\`.`, ephemeral: true, }); } - - // Find a channel with the same name as the course - const channel = await interaction.guild.channels.cache.find( - (c) => c.name.toLowerCase() === course.toLowerCase(), - ); - - // Otherwise, make sure that the channel exists, and is a text channel - if (channel === undefined) { - return await interaction.reply({ - content: `❌ | The course chat for \`${course}\` does not exist.`, - ephemeral: true, - }); - } else if (channel.type !== "GUILD_TEXT") { - return await interaction.reply({ - content: `❌ | The course chat for \`${course}\` is not a text channel.`, - ephemeral: true, - }); - } - - const permissions = new Permissions( - channel.permissionsFor(interaction.user.id).bitfield, - ); - - // Check if the member already has an entry in the channel's permission overwrites - if ( - !permissions.has([ - Permissions.FLAGS.VIEW_CHANNEL, - Permissions.FLAGS.SEND_MESSAGES, - ]) - ) { - return await interaction.reply({ - content: `❌ | You are not in the course chat for \`${course}\`.`, - ephemeral: true, - }); - } - - // Remove the member from the channel's permission overwrites - await channel.permissionOverwrites.delete(interaction.member); - - return await interaction.reply({ - content: `βœ… | Removed you from the course chat for \`${course}\`.`, - ephemeral: true, - }); } - return await interaction.reply("Error: invalid subcommand."); } catch (error) { await interaction.reply("Error: " + error); From b2562bec7aaed1b247db8edebf36ab2b1e140a28 Mon Sep 17 00:00:00 2001 From: Alexander C <113964162+zcDay1@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:46:24 +1000 Subject: [PATCH 11/14] Project descriptions (#154) * project-descriptions.js: updated course descriptions written by the directors for 2023 levelling up with Projects Fair Day, and the discord bot CTF flag easter egg entry under this command included too for this event. * made messages ephemeral to prevent chat from being spammed by users. And got rid of link embeds in the messages. * lint update --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> --- commands/project-descriptions.js | 112 ++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/commands/project-descriptions.js b/commands/project-descriptions.js index 0b186b0d..39f820eb 100644 --- a/commands/project-descriptions.js +++ b/commands/project-descriptions.js @@ -12,14 +12,17 @@ module.exports = { .addChoices([ ["Chaos", "chaos"], ["Circles", "circles"], - ["CS Electives", "cselectives"], + ["Uni-lectives", "unilectives"], ["Discord Bot", "discordbot"], ["Freerooms", "freerooms"], ["Jobsboard", "jobsboard"], ["Notangles", "notangles"], ["Structs.sh", "structs.sh"], + ["Trainee Program", "training-program"], ["UI/UX", "ui/ux"], + ["CMS", "cms"], ["Website", "website"], + ["???", "projects-fair-easter-egg-ctf"], ]), ), @@ -28,57 +31,100 @@ module.exports = { // console.log(`.${parsedOption}.`); switch (parsedOption) { case "chaos": - await interaction.reply( - "Chaos is a CSESoc internal recruitment tool written in Rust.", - ); + await interaction.reply({ + content: + "Chaos is an internal recruitment tool written in Rust. Are you allergic to google sheets and excel? Do you have nightmares from browsing through millions of lines of csv just to pick one applicant to take in? \nIntroducing Chaos, the ultimate lifesaver for clubs and societies! \nSay goodbye to the chaos and hello to simplicity. Chaos streamlines everything, making applications a breeze. \nWith a Rust πŸ¦€ backend, type-safe and secure, no more segfaults and losing data! \nOur minimalistic while aesthetic frontend interface frees your eyes and brains from the repetitive and dull rows and columns of data sheets πŸ“ƒ\n\n", + ephemeral: true, + }); break; case "circles": - await interaction.reply( - "Circles is a degree planner that helps you choose courses, plan out your terms and check progression.", - ); + await interaction.reply({ + content: + "Tired of using a poorly laid out spreadsheet to cobble together a course progression plan to follow for the next 3-8 years of your life? Have no fear, Circles is here! \nCircles is a UNSW degree planner where you can explore and validate your degree structure. \nYou can find and use a live build of Circles at ", + ephemeral: true, + }); break; - case "cselectives": - await interaction.reply( - "Unsure about what a course is like? Worry no more; CSElectives lets you read and write reviews of UNSW CSE courses.", - ); + case "unilectives": + await interaction.reply({ + content: + "Tired of searching through websites and forum posts to find the perfect course? Only to discover that it's offered once a year? Or perhaps the workload turned out to be completely different from your expectations? \nLook no further, Uni-lectives has got your back. With 1000 unique reviews and counting across a variety of faculties, Uni-lectives is your one stop shop for UNSW courses and electives, where you can access valuable reviews and also contribute your own, empowering others to make informed choices about the courses they enrol in!\n\n", + ephemeral: true, + }); break; case "discordbot": - await interaction.reply( - "CSESoc Discord Bot is your friendly helper in all things fun and CSE.", - ); + await interaction.reply({ + content: + "Discord Bot is your friendly CSE discord companion on the CSESoc discord server, offering various features such as checking what week it is, explaining what all CSESoc/DevSoc Projects do, the 24 minigame and more to come! \nHere's a sneak peek at coming features: \n\nWeekly Lunch buddy - a speed friending feature for organising and meeting up on-campus each week with like-minded friendly people! \nSydney Trains Delay API - conveniently check if the light rail is down from the comfort of your study room before you get hit with a nasty surprise at Anzac Parade or High Street!", + ephemeral: true, + }); break; case "freerooms": - await interaction.reply( - "Looking for a room to study in? Freerooms lets you see which on-campus rooms are vacant and which ones are booked.", - ); + await interaction.reply({ + content: + "Freerooms is a tool designed to help UNSW students find empty or unbooked rooms on campus.\n\nπŸ₯ΎHave you ever wandered around campus, searching for an empty study room?πŸšͺ Have you ever wanted to study somewhere other than the weird smelling ASB🏒, the loud corridors of Ainsworth 🏦 or the poorly decorated main library? πŸ“š \nIf you are a director or exec, have you ever wanted to find a room for your in-person meetings or society event? \nWhether you're in need of a quiet study nook or a large space for your society's next big event, Freerooms has got you covered!\n\n", + ephemeral: true, + }); break; case "jobsboard": - await interaction.reply( - "Jobsboard is an app that connects CSE students with companies looking for recruits.", - ); + await interaction.reply({ + content: + "Are you tired of hearing your friends talk about their exciting summer internship experiences while feeling left out? Fear not, because Jobsboard has got your back so you can wave goodbye to spending your summer working on projects to put on your resume! \nSupported by CSESoc’s strong partnerships with top tech giants in Australia like Atlassian, IMC, Canva and more, you will have immediate access to opportunities from these companies as soon they become available on Jobsboard!\n\n", + ephemeral: true, + }); break; case "notangles": - await interaction.reply( - "Notangles is a timetable planning app for UNSW students to build their perfect timetable, even before class registration opens.", - ); + await interaction.reply({ + content: + "Class registrations out and you have no clue how your next term is going to pan out? No idea how to come up with a timetable that balances all your classes and social events that you cannot miss? Do not worry! Notangles got your back. \nNotangles is your interactive timetable application, that can help you and your friends plan out a weekly schedule by showing you available classes for your courses and allow you to also slot in recurring events that can not be missed. It can also generate a timetable for you by taking in your preferences. Let there be no more timetable-tangles with Notangles!\n\n", + ephemeral: true, + }); break; case "structs.sh": - await interaction.reply("Structs.sh is an interactive algorithm visualiser."); + await interaction.reply({ + content: + "Structs.sh is an educational tool for computer science students that visualizes the most fundamental data structures (arrays, linked lists and binary search trees) and algorithms (sorting, searching and traversal). \nThe 2023 team is committed to transforming your educational experience by developing an application never seen before: a visual debugger that lets users type in arbitrary C code for our website to visualize the data structure(s) present in memory.\n\n", + ephemeral: true, + }); + break; + case "training-program": + await interaction.reply({ + content: + "The Training Program is a 1 term crash-course built to train up students new to or interested in web-dev! Every term, we teach the basics of React and JS, then put trainees into groups led by our talented training leads to build a personal project of their own! These personal projects can be anything that you think of, ranging from productivity web apps πŸ“† to dating apps built just for computer science students 😳. The training program is a place for learning new skills and getting you started on building that new tech idea you've always been thinking about!\n\nCome join today!", + ephemeral: true, + }); break; case "ui/ux": - await interaction.reply( - "The CSESoc Development UI/UX team works with all things related to user interface and experience design!", - ); + await interaction.reply({ + content: + "The CSESoc Development UI/UX team works with all things related to user interface and experience design!", + ephemeral: true, + }); + break; + case "projects-fair-easter-egg-ctf": + await interaction.reply({ + content: "Good job! Ollie's easter egg is levelup{discordbot_and_Ollie}", + ephemeral: true, + }); + break; + case "cms": + await interaction.reply({ + content: + "Each year CSESoc creates and publishes a number of blogs, articles, and guides dedicated to exploring interesting topics and helping students with their studies. The CMS aims to make creating these documents easier and more efficient by unifying the system used across portfolios. \nThis year the team has focused on developing the fundamental building blocks blog writers may need such as having sections of code within documents. The CMS team continues to evolve the application with the objective of having concurrent editing capabilities.", + ephemeral: true, + }); break; case "website": - await interaction.reply( - "The website team are in charge of writing the software for the CSESoc website.", - ); + await interaction.reply({ + content: + "Representing the CSE Society, the website showcases the main features of the community and collates all relevant resources in an easily located manner. Decorated with links to portfolios, guides, sponsors, and relevant social media platforms, the website allows all students to quickly navigate to the service they require.\n\n", + ephemeral: true, + }); break; default: - await interaction.reply( - "Error: the switch case has fallen through to the default case.", - ); + await interaction.reply({ + content: "Error: the switch case has fallen through to the default case.", + ephemeral: true, + }); break; } }, From 65f32ef362f70c7e27762339d12e1eb40f92cb2c Mon Sep 17 00:00:00 2001 From: Bigbugman <101852152+Bigbugman@users.noreply.github.com> Date: Sat, 21 Oct 2023 22:27:43 +1100 Subject: [PATCH 12/14] Dev/course channel fix 3 (#158) * course-chat-fix-2, make new roles where there arent any in /rolespermoverride and also remove individual permission overwrites, and also updates to /course join to catch when there doesn't exist a role for a course and also /course leave to attempt to remove individual permission overwrites along with removing course role PLEASE TEST THOROUGHLY AND INSPECT CODE THOROUGHLY. I wrote this ASAP and haven't got time to properly review it. * Modified functions for course channel fixes * Check for if user only has read perms for overwrite replacing and fix looping over members * Fix to functionality of iteration and editing roles * Fix to initial working state * Fix linting * Additional fixes to ordering and component operations to remove role and individual permissions correctly * Appended filter for both view only perms and view + send messages perms --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> Co-authored-by: Wolfdragon24 --- commands/course.js | 63 ++++++++++++++++++---- commands/rolesPermOverride.js | 98 ++++++++++++++++++----------------- 2 files changed, 103 insertions(+), 58 deletions(-) diff --git a/commands/course.js b/commands/course.js index b55544c1..dac19ba6 100644 --- a/commands/course.js +++ b/commands/course.js @@ -1,7 +1,5 @@ const { SlashCommandBuilder } = require("@discordjs/builders"); -const { Permissions } = require("discord.js"); -const MODERATION_REQUEST_CHANNEL = 824506830641561600; const COMMAND_JOIN = "join"; const COMMAND_LEAVE = "leave"; @@ -54,6 +52,9 @@ const is_valid_course = (course) => { ); }; +const in_overwrites = (overwrites, id) => + [1024n, 3072n].includes(overwrites.find((v, k) => k === id)?.allow?.bitfield); + module.exports = { data: new SlashCommandBuilder() .setName("course") @@ -128,8 +129,9 @@ module.exports = { }); } + // if there isn't a role that matches the name of the course return await interaction.reply({ - content: `βœ… | End of command - ${course_with_alias}.`, + content: `There doesn't exist a role for \`${course_with_alias}\`. If you believe there should be, please inform a member of the Discord Bot team or staff.`, ephemeral: true, }); } else if (interaction.options.getSubcommand() === COMMAND_LEAVE) { @@ -143,24 +145,65 @@ module.exports = { }); } - // First, let's see if there's a role that matches the name of the course + // Check and fetch a channel corresponding to input + const channel = await interaction.guild.channels.cache.find( + (c) => c.name.toLowerCase() === course.toLowerCase(), + ); + + if (channel === undefined) { + return await interaction.reply({ + content: `❌ | The course chat for \`${course}\` does not exist.`, + ephemeral: true, + }); + } else if (channel.type !== "GUILD_TEXT") { + return await interaction.reply({ + content: `❌ | The course chat for \`${course}\` is not a text channel.`, + ephemeral: true, + }); + } + + const permissions = channel.permissionOverwrites.cache; + + // Then check if there's a role that matches the name of the course const role = await interaction.guild.roles.cache.find( (r) => r.name.toLowerCase() === course.toLowerCase(), ); - // If there is, let's see if the member already has that role + // Check if the role exists if (role !== undefined) { - if (!interaction.member.roles.cache.has(role.id)) { + // Check if the member has access via individual perms + if (in_overwrites(permissions, interaction.member.id)) { + // Remove the member from the channel's permission overwrites if so + await channel.permissionOverwrites.delete(interaction.member); + } + + // Check if the member has access via role + if ( + interaction.member.roles.cache.has(role.id) && + in_overwrites(permissions, role.id) + ) { + // If they do remove the role + await interaction.member.roles.remove(role); + return await interaction.reply({ + content: `βœ… | Removed you from the role and chat for \`${course}\`.`, + ephemeral: true, + }); + } else { return await interaction.reply({ content: `❌ | You do not have the role for \`${course}\`.`, ephemeral: true, }); } - - // If they do, let's remove the role from them - await interaction.member.roles.remove(role); + } else if (in_overwrites(permissions, interaction.member.id)) { + // Check if the user has individual perms and removes if so + await channel.permissionOverwrites.delete(interaction.member); + return await interaction.reply({ + content: `βœ… | Removed you from the chat for \`${course}\`.`, + ephemeral: true, + }); + } else { return await interaction.reply({ - content: `βœ… | Removed you from the role and chat for \`${course}\`.`, + content: `❌ | You do not have access to the chat for \`${course}\`.`, ephemeral: true, }); } diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js index 21e05e75..293d7994 100644 --- a/commands/rolesPermOverride.js +++ b/commands/rolesPermOverride.js @@ -1,7 +1,7 @@ const { SlashCommandBuilder } = require("@discordjs/builders"); const { Permissions } = require("discord.js"); -const is_valid_course = (course) => { +const is_valid_course_name = (course) => { const reg_comp_course = /^comp\d{4}$/; const reg_math_course = /^math\d{4}$/; const reg_binf_course = /^binf\d{4}$/; @@ -18,47 +18,51 @@ const is_valid_course = (course) => { ); }; -function editChannels(interaction, channels, role) { - channels.forEach((channel) => { - if ( - channel.type === "GUILD_TEXT" && - channel.name.toLowerCase() === role.name.toLowerCase() - ) { - // Remove all permissions from a role - role.setPermissions(0n) - .then((updated) => - console.log(`Updated permissions to ${updated.permissions.bitfield}`), - ) - .catch(console.error); - // Set the permissions of the role - // Add the member to the channel's permission overwrites - channel.permissionOverwrites.create(role, { - VIEW_CHANNEL: true, - SEND_MESSAGES: true, +const in_overwrites = (overwrites, id) => + [1024n, 3072n].includes(overwrites.find((v, k) => k === id)?.allow?.bitfield); + +async function editChannels(interaction, channels) { + for (const data of channels) { + const channel = data[1]; + const is_valid = is_valid_course_name(channel.name); + + if (!is_valid || channel.type !== "GUILD_TEXT") continue; + + let role = interaction.guild.roles.cache.find( + (r) => r.name.toLowerCase() === channel.name.toLowerCase(), + ); + + if (!role) { + role = await interaction.guild.roles.create({ + name: channel.name.toLowerCase(), + color: "BLUE", }); - console.log(channel.name, role.name); } - }); -} -function editRoles(interaction, roles) { - roles.forEach((role) => { - if (is_valid_course(role.name)) { - interaction.guild.channels - .fetch() - .then( - (channels) => ( - editChannels(interaction, channels, role), - console.log(`There are ${channels.size} channels.`) - ), - ) - .catch(console.error); + const permissions = channel.permissionOverwrites.cache; + + // clear every individual user permission overwrite for the channel + for (const user of channel.members) { + const userId = user[0]; + const userObj = user[1]; + + if (userObj.user.bot) continue; + + // Check if the member has access via individual perms + if (in_overwrites(permissions, userId)) { + // Remove the member from the channel's permission overwrites + await channel.permissionOverwrites.delete(userObj); + await userObj.roles.add(role); + } } - }); - interaction.reply({ - content: `βœ… | found course chats and matching roles, cleared and set permission overwrites for roles.`, - ephemeral: true, - }); + + // set the permissions for the new role on a channel + // const channel = interaction.guild.channels.cache.get('CHANNEL_ID'); + await channel.permissionOverwrites.create(role, { + VIEW_CHANNEL: true, + SEND_MESSAGES: true, + }); + } } module.exports = { @@ -75,19 +79,17 @@ module.exports = { ephemeral: true, }); } + await interaction.deferReply(); + // for all roles with name == chat name involving 4 letter prefix comp, seng, engg or binf, - // give the role the permission override to participate in the matching channel. - interaction.guild.roles - .fetch() - .then( - (roles) => ( - editRoles(interaction, roles), console.log(`There are ${roles.size} roles.`) - ), - ) - .catch(console.error); + // Get all channels and run function + const channels = await interaction.guild.channels.fetch(); + + await editChannels(interaction, channels); + await interaction.editReply("Successfully ported all user permissions to roles."); } catch (error) { - await interaction.reply("Error: " + error); + await interaction.editReply("Error: " + error); } }, }; From 027f8fe8869f3c6c5fcb6c4a1bb1a3b10f65dd93 Mon Sep 17 00:00:00 2001 From: Abraham Assariparambil Earnest <44077482+abe123442@users.noreply.github.com> Date: Sun, 22 Oct 2023 12:23:08 +1100 Subject: [PATCH 13/14] dev/course-channel-fix-5 (#160) * course-chat-fix-2, make new roles where there arent any in /rolespermoverride and also remove individual permission overwrites, and also updates to /course join to catch when there doesn't exist a role for a course and also /course leave to attempt to remove individual permission overwrites along with removing course role PLEASE TEST THOROUGHLY AND INSPECT CODE THOROUGHLY. I wrote this ASAP and haven't got time to properly review it. * Modified functions for course channel fixes * Check for if user only has read perms for overwrite replacing and fix looping over members * Fix to functionality of iteration and editing roles * Fix to initial working state * Fix linting * Additional fixes to ordering and component operations to remove role and individual permissions correctly * Appended filter for both view only perms and view + send messages perms * Appended single channel mode for permissions override * Appended check command for role permissions * Resolved merge conflicts in dev/course-channel-fix-4 --------- Co-authored-by: AcdSoftCo <106219586+AcdSoftCo@users.noreply.github.com> Co-authored-by: Wolfdragon24 --- commands/rolesPermOverride.js | 101 ++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js index 7d72d0af..0c8e65b6 100644 --- a/commands/rolesPermOverride.js +++ b/commands/rolesPermOverride.js @@ -65,11 +65,68 @@ async function editChannels(interaction, channels) { } } +async function isFixed(interaction, channel) { + const is_valid = is_valid_course_name(channel.name); + + if (!is_valid || channel.type !== "GUILD_TEXT") return true; + + const role = interaction.guild.roles.cache.find( + (r) => r.name.toLowerCase() === channel.name.toLowerCase(), + ); + + if (!role) return false; + + const permissions = channel.permissionOverwrites.cache; + + // clear every individual user permission overwrite for the channel + for (const user of channel.members) { + const userId = user[0]; + const userObj = user[1]; + + if (userObj.user.bot) continue; + + // Check if the member has access via individual perms + if (in_overwrites(permissions, userId)) return false; + } + + if (!in_overwrites(permissions, role.id)) return false; + + return true; +} + +async function allFixed(interaction, channels) { + const unfixed = []; + for (const data of channels) { + const channel = data[1]; + const fixed = await isFixed(interaction, channel); + + if (!fixed) unfixed.push(channel.name); + } + + return unfixed; +} + module.exports = { data: new SlashCommandBuilder() .setName("rolespermoverride") .setDescription( "Looks for matches between roles and course chats and attaches permissions.", + ) + .addBooleanOption((option) => + option + .setName("singlechannel") + .setDescription( + "Should this command only be run on the current channel? (Default: False)", + ) + .setRequired(false), + ) + .addBooleanOption((option) => + option + .setName("check") + .setDescription( + "Should a check be run on if the channel is fixed? (Default: False)", + ) + .setRequired(false), ), async execute(interaction) { try { @@ -79,16 +136,48 @@ module.exports = { ephemeral: true, }); } - await interaction.deferReply(); // for all roles with name == chat name involving 4 letter prefix comp, seng, engg or binf, - // Get all channels and run function - const channels = await interaction.guild.channels.fetch(); - - await editChannels(interaction, channels); - await interaction.editReply("Successfully ported all user permissions to roles."); + if (!interaction.options.getBoolean("singlechannel")) { + // Get all channels and run specified function + const channels = await interaction.guild.channels.fetch(); + + if (!interaction.options.getBoolean("check")) { + await editChannels(interaction, channels); + await interaction.editReply( + "Successfully ported all user permissions to roles.", + ); + } else { + const unfixed = await allFixed(interaction, channels); + + if (unfixed.length == 0) { + await interaction.editReply("All channels in this server appear fixed."); + } else { + await interaction.editReply( + `The following channels appear unfixed: ${unfixed.join(", ")}`, + ); + } + } + } else { + const channel = interaction.channel; + + if (!interaction.options.getBoolean("check")) { + await editChannels(interaction, [[undefined, channel]]); + await interaction.editReply( + "Successfully ported user permissions to roles in this channel", + ); + } else { + const fixed = await isFixed(interaction, channel); + + if (fixed) { + await interaction.editReply("This channel appears fixed."); + } else { + await interaction.editReply("This channel appears unfixed."); + } + } + } } catch (error) { await interaction.editReply("Error: " + error); } From 4bac7ae9718eddef5934e7499d7ba3f7ab045748 Mon Sep 17 00:00:00 2001 From: Alexander C <113964162+zcDay1@users.noreply.github.com> Date: Tue, 24 Oct 2023 23:21:47 +1100 Subject: [PATCH 14/14] Update rolesPermOverride.js double equals updated to triple equals on line 156 --- commands/rolesPermOverride.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/rolesPermOverride.js b/commands/rolesPermOverride.js index f6787e53..6e655b3a 100644 --- a/commands/rolesPermOverride.js +++ b/commands/rolesPermOverride.js @@ -153,7 +153,7 @@ module.exports = { } else { const unfixed = await allFixed(interaction, channels); - if (unfixed.length == 0) { + if (unfixed.length === 0) { await interaction.editReply("All channels in this server appear fixed."); } else { await interaction.editReply(