- Disable unfinished CDR commands
- Impliment delayed auto-delete of orphaned extensions, with auto cancel.
This commit is contained in:
parent
32cfeb354e
commit
68d6402ffd
48
commands.js
48
commands.js
|
@ -105,6 +105,16 @@ module.exports = [
|
||||||
"description": "Reboot the server (LAST RESORT)",
|
"description": "Reboot the server (LAST RESORT)",
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"default_member_permissions": 0
|
"default_member_permissions": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "list-deletions",
|
||||||
|
"description": "List pending deletions",
|
||||||
|
"type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "check-deletions",
|
||||||
|
"description": "Check for orphaned extensions",
|
||||||
|
"type": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -164,25 +174,25 @@ module.exports = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
"name": "cdr",
|
// "name": "cdr",
|
||||||
"description": "Get the call detail records for your extension",
|
// "description": "Get the call detail records for your extension",
|
||||||
"type": 1,
|
// "type": 1,
|
||||||
"options": [
|
// "options": [
|
||||||
{
|
// {
|
||||||
"name": "start_date",
|
// "name": "start_date",
|
||||||
"description": "The start date for the CDR (mm/dd/yyyy)",
|
// "description": "The start date for the CDR (mm/dd/yyyy)",
|
||||||
"type": 3,
|
// "type": 3,
|
||||||
"required": false
|
// "required": false
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
"name": "end_date",
|
// "name": "end_date",
|
||||||
"description": "The end date for the CDR (mm/dd/yyyy)",
|
// "description": "The end date for the CDR (mm/dd/yyyy)",
|
||||||
"type": 3,
|
// "type": 3,
|
||||||
"required": false
|
// "required": false
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
"name": "lookup",
|
"name": "lookup",
|
||||||
"description": "Find extension by Discord user",
|
"description": "Find extension by Discord user",
|
||||||
|
|
48
deletions.js
Normal file
48
deletions.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const pool = global.pool
|
||||||
|
const fpbx = global.fpbx
|
||||||
|
const client = global.client
|
||||||
|
const log = global.log
|
||||||
|
|
||||||
|
module.exports = {};
|
||||||
|
|
||||||
|
module.exports.handleScheduled = async () => {
|
||||||
|
const deletions = await pool.query('SELECT * FROM discord_deletions');
|
||||||
|
if (!deletions) return;
|
||||||
|
|
||||||
|
for (const deletion of deletions) {
|
||||||
|
const guild = client.guilds.cache.get(process.env.DISCORD_GUILD);
|
||||||
|
const member = guild ? await guild.members.fetch(deletion.discordId).catch(() => null) : null;
|
||||||
|
|
||||||
|
if (member) {
|
||||||
|
log.info(`User ${deletion.discordId} rejoined, removing deletion`);
|
||||||
|
await pool.query('DELETE FROM discord_deletions WHERE discordId = ?', [deletion.discordId]);
|
||||||
|
return;
|
||||||
|
} else if (new Date(deletion.deleteAt) < new Date()) {
|
||||||
|
log.info(`Deleting extension for ${deletion.discordId}`);
|
||||||
|
await fpbx.deleteExtension(deletion.extension);
|
||||||
|
await fpbx.reload();
|
||||||
|
await pool.query('DELETE FROM discord_deletions WHERE discordId = ?', [deletion.discordId]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.findOrphans = async () => {
|
||||||
|
const users = await pool.query('SELECT * FROM discord_users');
|
||||||
|
const deletions = await pool.query('SELECT * FROM discord_deletions');
|
||||||
|
const guild = client.guilds.cache.get(process.env.DISCORD_GUILD);
|
||||||
|
if (!users) return
|
||||||
|
for (const user of users) {
|
||||||
|
const member = guild ? await guild.members.fetch(user.discordId).catch(() => null) : null;
|
||||||
|
if (!member) {
|
||||||
|
log.info(`User ${user.discordId} not found in guild, marking for deletion`);
|
||||||
|
const isMarkedForDeletion = deletions.some(deletion => deletion.discordId === user.discordId);
|
||||||
|
if (isMarkedForDeletion) {
|
||||||
|
log.info(`User ${user.discordId} is already marked for deletion`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const deleteAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour in the future
|
||||||
|
await pool.query('INSERT INTO discord_deletions (discordId, extension, deleteAt) VALUES (?, ?, ?)', [user.discordId, user.extension, deleteAt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
48
index.js
48
index.js
|
@ -1,5 +1,5 @@
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
const cron = require("node-cron")
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const mariadb = require("mariadb");
|
const mariadb = require("mariadb");
|
||||||
const pool = mariadb.createPool({
|
const pool = mariadb.createPool({
|
||||||
|
@ -53,6 +53,8 @@ global.fpbx = fpbx;
|
||||||
global.client = client;
|
global.client = client;
|
||||||
global.log = log;
|
global.log = log;
|
||||||
|
|
||||||
|
const deletion = require("./deletions.js");
|
||||||
|
|
||||||
client.on('ready', async () => {
|
client.on('ready', async () => {
|
||||||
log.success(`Logged in as ${client.user.displayName}`);
|
log.success(`Logged in as ${client.user.displayName}`);
|
||||||
const commands = require("./commands")
|
const commands = require("./commands")
|
||||||
|
@ -84,6 +86,25 @@ client.on('ready', async () => {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
cron.schedule('* * * * *', () => {
|
||||||
|
try {
|
||||||
|
deletion.handleScheduled();
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Failed to execute deletion task: ${error}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cron.schedule('0 * * * *', () => {
|
||||||
|
log.info("Checking for orphaned extensions...");
|
||||||
|
try {
|
||||||
|
deletion.findOrphans();
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Failed to execute orphan task: ${error}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
deletion.findOrphans();
|
||||||
|
deletion.handleScheduled();
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('interactionCreate', async interaction => {
|
client.on('interactionCreate', async interaction => {
|
||||||
|
@ -116,6 +137,31 @@ client.on('interactionCreate', async interaction => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on('guildMemberAdd', async member => {
|
||||||
|
log.info(`User ${member.id} joined`);
|
||||||
|
const [deletion] = await pool.query('SELECT * FROM discord_deletions WHERE discordId = ?', [member.id]);
|
||||||
|
if (deletion) {
|
||||||
|
await pool.query('DELETE FROM discord_deletions WHERE discordId = ?', [member.id]);
|
||||||
|
log.info(`User ${member.id} rejoined, removing deletion`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('guildMemberRemove', async member => {
|
||||||
|
log.info(`User ${member.id} left`);
|
||||||
|
const [deletion] = await pool.query('SELECT * FROM discord_deletions WHERE discordId = ?', [member.id]);
|
||||||
|
const [user] = await pool.query('SELECT * FROM discord_users WHERE discordId = ?', [member.id]);
|
||||||
|
if (!user) return;
|
||||||
|
log.info("User has extension...")
|
||||||
|
log.debug(typeof(deletion))
|
||||||
|
log.debug(JSON.stringify(deletion))
|
||||||
|
if (!deletion) {
|
||||||
|
log.info("No deletion, adding")
|
||||||
|
const deleteAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour in the future
|
||||||
|
await pool.query('INSERT INTO discord_deletions (discordId, extension, deleteAt) VALUES (?, ?, ?)', [member.id, user.extension, deleteAt]);
|
||||||
|
log.info(`User ${member.id} left, marking for deletion`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (fs.existsSync("./import.json")) {
|
if (fs.existsSync("./import.json")) {
|
||||||
const importData = JSON.parse(fs.readFileSync("./import.json", "utf8"));
|
const importData = JSON.parse(fs.readFileSync("./import.json", "utf8"));
|
||||||
|
|
||||||
|
|
|
@ -64,5 +64,18 @@ module.exports.execute = async (interaction) => {
|
||||||
});
|
});
|
||||||
runCommand('reboot 0');
|
runCommand('reboot 0');
|
||||||
break;
|
break;
|
||||||
|
case 'list-deletions':
|
||||||
|
const deletions = await pool.query('SELECT * FROM discord_deletions');
|
||||||
|
if (!deletions.length) {
|
||||||
|
await interaction.reply({ content: 'No pending deletions.', ephemeral: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletionList = deletions.map((deletion) => {
|
||||||
|
return `Discord ID: ${deletion.discordId}, Extension: ${deletion.extension}, Delete At: <t:${deletion.deleteAt / 1000}>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.reply({ content: deletionList.join('\n'), ephemeral: true });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,8 +11,9 @@ module.exports.execute = async (interaction) => {
|
||||||
await interaction.reply({ content: `We're sorry, It doesn't look like you have an extension!`, ephemeral: true });
|
await interaction.reply({ content: `We're sorry, It doesn't look like you have an extension!`, ephemeral: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
const extInfo = await fpbx.getExtension(lookup.extension);
|
const extInfo = await fpbx.getExtension(lookup.extension);
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
ephemeral: true, embeds: [
|
ephemeral: true, embeds: [
|
||||||
{
|
{
|
||||||
title: "Your Extension Info",
|
title: "Your Extension Info",
|
||||||
|
|
5
migrations/002_init_deletions_table.sql
Normal file
5
migrations/002_init_deletions_table.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS discord_deletions (
|
||||||
|
extension VARCHAR(20) PRIMARY KEY,
|
||||||
|
discordId VARCHAR(25) NOT NULL,
|
||||||
|
deleteAt TIMESTAMP NOT NULL
|
||||||
|
);
|
22
package-lock.json
generated
22
package-lock.json
generated
|
@ -15,6 +15,7 @@
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"freepbx-graphql-client": "^0.1.1",
|
"freepbx-graphql-client": "^0.1.1",
|
||||||
"mariadb": "^3.2.0",
|
"mariadb": "^3.2.0",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"ping": "^0.4.4",
|
"ping": "^0.4.4",
|
||||||
"sqlite3": "^5.1.4",
|
"sqlite3": "^5.1.4",
|
||||||
"ssh2": "^1.15.0"
|
"ssh2": "^1.15.0"
|
||||||
|
@ -1247,6 +1248,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
|
||||||
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
|
"integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/node-cron": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": "8.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
@ -1725,6 +1738,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"start": "node index.js"
|
"start": "node index.js"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"freepbx-graphql-client": "^0.1.1",
|
"freepbx-graphql-client": "^0.1.1",
|
||||||
"mariadb": "^3.2.0",
|
"mariadb": "^3.2.0",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"ping": "^0.4.4",
|
"ping": "^0.4.4",
|
||||||
"sqlite3": "^5.1.4",
|
"sqlite3": "^5.1.4",
|
||||||
"ssh2": "^1.15.0"
|
"ssh2": "^1.15.0"
|
||||||
|
|
Loading…
Reference in a new issue