diff --git a/commands.js b/commands.js index 605780f..60b0c99 100644 --- a/commands.js +++ b/commands.js @@ -303,5 +303,10 @@ module.exports = [ "type": 1 } ] + }, + { + "name": "reset", + "description": "Reset or change your extension password", // Opens a modal to reset password + "type": 1 } ] \ No newline at end of file diff --git a/freepbx.js b/freepbx.js index 4d79374..b4232bf 100644 --- a/freepbx.js +++ b/freepbx.js @@ -155,24 +155,45 @@ class FreepbxManager { return await this.pbxCall(query); } - // async updateName(ext, name) { - // const query = gql` - // mutation updateName($ext: ID!, $name: String!) { - // updateExtension(input: {extensionId: $ext, name: $name}) { - // status, - // message - // } - // }`; + async updateExtName(ext, name) { + const query = gql` + mutation updateExt($ext: ID!, $name: String, $password: String) { + updateExtension(input: { + extensionId: $ext + name: $name + }) { + status, + message + } + }`; - // const variables = { - // ext, - // name, - // }; + const variables = { + ext, + endName, + }; - // return await this.pbxCall(query, variables); - // } + return await this.pbxCall(query, variables); + } // TODO: Implement updateName method, Current implementation resets extension for some reason + async updateExtPassword(ext, password) { + const query = gql` + mutation updateExt($ext: ID!, $name: String, $password: String) { + updateExtension(input: { + extensionId: $ext + extPassword: $password + }) { + status, + message + } + }`; + const variables = { + ext, + password, + }; + return await this.pbxCall(query, variables); + } + async joinPageGroup(ext, pageGroup) { const [lookup] = await this.pool.query('SELECT * FROM paging_groups WHERE page_number = ? AND ext = ?', [pageGroup, ext]); if (lookup) { diff --git a/index.js b/index.js index 933d9ba..9cbfd22 100644 --- a/index.js +++ b/index.js @@ -138,6 +138,24 @@ client.on('interactionCreate', async interaction => { });; } break; + case Discord.InteractionType.ModalSubmit: + const modal = require(`./interactionHandlers/modals/${interaction.customId}`); + + if (!modal) return; + + try { + await modal.execute(interaction); + } catch (error) { + log.error(error); + await interaction.reply({ content: 'There was an error while executing this modal!', ephemeral: true }).catch((error) => { + log.error(`Failed to inform user of error: ${error}`); + }); + } + break; + default: { + log.warn(`Unknown interaction type received: ${interaction.type}`); + return; + } } }); diff --git a/interactionHandlers/commands/button.js b/interactionHandlers/commands/button.js index e8ff8f9..2d31949 100644 --- a/interactionHandlers/commands/button.js +++ b/interactionHandlers/commands/button.js @@ -94,6 +94,13 @@ module.exports.execute = async (interaction) => { emoji: "â„šī¸", style: Discord.ButtonStyle.Primary, custom_id: "getExtensionInfo" + }, + { + type: Discord.ComponentType.Button, + label: "Reset Password", + emoji: "🔄", + style: Discord.ButtonStyle.Danger, + custom_id: "resetPassword" } ] } diff --git a/interactionHandlers/commands/reset.js b/interactionHandlers/commands/reset.js new file mode 100644 index 0000000..7745feb --- /dev/null +++ b/interactionHandlers/commands/reset.js @@ -0,0 +1,3 @@ +module.exports = {}; + +module.exports.execute = require("../common/resetPasswordDialog").execute; \ No newline at end of file diff --git a/interactionHandlers/common/getExtInfo.js b/interactionHandlers/common/getExtInfo.js index c66f3af..f7c5604 100644 --- a/interactionHandlers/common/getExtInfo.js +++ b/interactionHandlers/common/getExtInfo.js @@ -20,6 +20,20 @@ module.exports.execute = async (interaction) => { description: `**PBX Address:** \`${process.env.PBX_HOSTNAME}\`\n**Extension/Username:** \`${extInfo.fetchExtension.user.extension}\`\n**Name:** \`${extInfo.fetchExtension.user.name}\`\n**Password:** ||\`${extInfo.fetchExtension.user.extPassword}\`||`, color: 0x00ff00 } + ], + components: [ + { + type: 1, + components: [ + { + type: Discord.ComponentType.Button, + label: "Reset Password", + emoji: "🔄", + style: Discord.ButtonStyle.Danger, + custom_id: "resetPassword" + } + ] + } ] }); diff --git a/interactionHandlers/common/resetPasswordDialog.js b/interactionHandlers/common/resetPasswordDialog.js new file mode 100644 index 0000000..4ff327f --- /dev/null +++ b/interactionHandlers/common/resetPasswordDialog.js @@ -0,0 +1,36 @@ +const pool = global.pool +const fpbx = global.fpbx +const client = global.client +const log = global.log + +module.exports = {}; + +module.exports.execute = async (interaction) => { + const [lookup] = await pool.query('SELECT * FROM discord_users WHERE discordId = ?', [interaction.user.id]); + if (!lookup) { + await interaction.reply({ content: `We're sorry, It doesn't look like you have an extension!`, ephemeral: true }); + return; + } + await interaction.deferReply({ ephemeral: true }); + interaction.showModal({ + title: 'Reset Extension Password', + customId: 'resetPasswordModal', + components: [ + { + type: 18, // Label + label: "Please enter a new password for your extension. Leave blank to generate a random one.", + compoents: [ + { + type: 4, // Text Input + id: 'newPassword', + style: 1, + min_length: 6, + max_length: 64, + placeholder: 'New Password', + required: false + } + ] + } + ] + }); +} \ No newline at end of file diff --git a/interactionHandlers/components/resetPassword.js b/interactionHandlers/components/resetPassword.js new file mode 100644 index 0000000..7745feb --- /dev/null +++ b/interactionHandlers/components/resetPassword.js @@ -0,0 +1,3 @@ +module.exports = {}; + +module.exports.execute = require("../common/resetPasswordDialog").execute; \ No newline at end of file diff --git a/interactionHandlers/modals/resetPasswordModal.js b/interactionHandlers/modals/resetPasswordModal.js new file mode 100644 index 0000000..f709341 --- /dev/null +++ b/interactionHandlers/modals/resetPasswordModal.js @@ -0,0 +1,29 @@ +const pool = global.pool +const fpbx = global.fpbx +const client = global.client +const log = global.log +module.exports = {}; + +module.exports.execute = async (interaction) => { + const [lookup] = await pool.query('SELECT * FROM discord_users WHERE discordId = ?', [interaction.user.id]); + if (!lookup) { + await interaction.reply({ content: `We're sorry, It doesn't look like you have an extension!`, ephemeral: true }); + return; + } + await interaction.deferReply({ ephemeral: true }); + const newPassword = interaction.fields.getTextInputValue('newPassword'); + let passwordToSet = newPassword; + if (!newPassword || newPassword.trim() === '') { + // Generate password based on process.env.PASSWORD_LENGTH and process.env.PASSWORD_CHARS or default to 32 alphanumeric with caps + const length = parseInt(process.env.PASSWORD_LENGTH) || 32; + const chars = process.env.PASSWORD_CHARS || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + passwordToSet = Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join(''); + } + try { + await fpbx.updateExtPassword(lookup.extension, passwordToSet); + await interaction.editReply({ content: `Your extension password has been reset successfully! Your new password is: ||\`${passwordToSet}\`||`, ephemeral: true }); + } catch (error) { + log.error(error); + await interaction.editReply({ content: 'There was an error while resetting your extension password!', ephemeral: true }); + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a7afd78..d828b7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -704,15 +704,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -851,6 +842,7 @@ "version": "15.8.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "peer": true, "engines": { "node": ">= 10.x" }