Merge dev to main #1

Merged
ChrisChrome merged 20 commits from dev into main 2025-10-28 07:58:47 -06:00
3 changed files with 122 additions and 72 deletions

161
index.js
View file

@ -1,6 +1,25 @@
require("dotenv").config({quiet: true}); const blocked = [
const exec = require("child_process").exec; '0',
const Discord = require("discord.js"); '0000',
'9998',
'9999'
];
import dotenv from 'dotenv';
dotenv.config({ quiet: true })
import AMISocket from 'ami';
const sock = new AMISocket({
connect: {
host: '127.0.0.1',
port: 5038
},
credentials: {
username: process.env.AMI_USER,
secret: process.env.AMI_SECRET
},
events: true
})
import Discord from 'discord.js';
const hook = new Discord.WebhookClient({ url: process.env.DISCORD_WEBHOOK_URL }); const hook = new Discord.WebhookClient({ url: process.env.DISCORD_WEBHOOK_URL });
const cfTypes = { const cfTypes = {
@ -9,72 +28,92 @@ const cfTypes = {
CFU: "Unavailable", CFU: "Unavailable",
} }
const main = async () => { const main = () => {
const runAsterisk = (command) => var respData = [];
new Promise((resolve) => console.log('checking')
exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr })) // getList for CF, CFB, CFU
); sock.getList({
action: 'DBGetTree',
try { family: 'CF'
var forwards = {}; }).then(async (response) => {
const commands = [ // Responses are an array of objects. The first should always have eventlist: 'start', and last should have eventlist: 'complete'. Remove these, make an array of objects {key, val} based on the rest of the response objects.
'asterisk -x "database show CF"', let entries = response.filter(r => r.eventlist !== 'start' && r.eventlist !== 'Complete').map(r => ({ key: r.key, val: r.val }));
'asterisk -x "database show CFB"', for (let entry of entries) {
'asterisk -x "database show CFU"', let stuff = entry.key.split('/');
]; let type = stuff[1];
for (const command of commands) { let ext = stuff[2];
const { error, stdout, stderr } = await runAsterisk(command); console
if (error) { respData.push({
console.error(`Error executing command "${command}": ${error}`); extension: ext,
continue; type: type,
target: entry.val
});
} }
const lines = stdout.split('\n'); }).then(() => {
for (const line of lines) { sock.getList({
const match = line.match(/^\/(CF|CFB|CFU)\/(\d+)\s+:\s+(.+)$/); action: 'DBGetTree',
if (match) { family: 'CFB'
const type = match[1]; }).then(async (response) => {
const extension = match[2]; let entries = response.filter(r => r.eventlist !== 'start' && r.eventlist !== 'Complete').map(r => ({ key: r.key, val: r.val }));
const target = match[3]; for (let entry of entries) {
let stuff = entry.key.split('/');
forwards[`${type}/${extension}`] = target.replace(/\s+$/, ''); let type = stuff[1];
let ext = stuff[2];
console
respData.push({
extension: ext,
type: type,
target: entry.val
});
} }
}).then(() => {
sock.getList({
action: 'DBGetTree',
family: 'CFU'
}).then(async (response) => {
let entries = response.filter(r => r.eventlist !== 'start' && r.eventlist !== 'Complete').map(r => ({ key: r.key, val: r.val }));
for (let entry of entries) {
let stuff = entry.key.split('/');
let type = stuff[1];
let ext = stuff[2];
console
respData.push({
extension: ext,
type: type,
target: entry.val
});
} }
}).then(() => {
console.log(`Found ${respData.length} call forwards.`);
for (let cf of respData) {
// Check if forward target is anything in the 700-800 range, or is a number in the blocked list
if ((cf.target >= 700 && cf.target < 800) || blocked.includes(cf.target)) {
// Violation. Remove call forward from db.
console.log(`---> Violation found on extension ${cf.extension}. Removing call forward to ${cf.target}`);
sock.send({
action: 'DBDel',
family: cf.type,
key: cf.extension
}).then((resp) => {
console.log(`-----> Call forward removed successfully.`);
hook.send(`:no_entry: **Call Forward Removed** :no_entry:\nExtension **${cf.extension}** had a **${cfTypes[cf.type]}** call forward to **${cf.target}** The call forward has been removed.`);
})
} }
};
// Search all forwards for any target that is between 700 and 800. If it exists, log to console and run `asterisk -x "database del CF[U/B] <ext>"` to delete it setTimeout(main, 5000); // Repeat every 60 seconds
for (const [key, target] of Object.entries(forwards)) { });
const targetExt = parseInt(target, 10); });
if ((targetExt >= 700 && targetExt < 800) || targetExt === 0) { // Also block forwards to 0 (operator line) })
console.log(`Deleting forward ${key} to target ${target}`); }
await hook.send(`Fuckass with extension ${key.split('/')[1]} tried to forward to ${target}. ${cfTypes[key.split('/')[0]]} Forward has been deleted.`);
const delCommand = `asterisk -x "database del ${key.replace('/', ' ')}"`;
const { error, stdout, stderr } = await runAsterisk(delCommand);
if (error) {
console.error(`Error executing command "${delCommand}": ${error}`);
} else {
console.log(`Successfully deleted forward ${key}`);
}
}
}
} catch (err) {
console.error(`Unexpected error: ${err}`);
}
setTimeout(main, process.env.CHECK_INTERVAL * 1000); // Run every CHECK_INTERVAL seconds
};
const startup = async () => { const startup = async () => {
// Run `asterisk -x "core show version"` to get the Asterisk version. If it fails, wait 10 seconds before running main again sock.connect().then(() => {
exec('asterisk -x "core show version"', (error, stdout, stderr) => { console.log(sock.amiVersion)
if (error) {
console.error(`Error executing command: ${error}`);
console.log("Asterisk is not running. Retrying in 10 seconds...");
setTimeout(startup, 10000);
return;
}
const version = stdout.trim();
console.log(`Asterisk version: ${version}`);
main(); main();
}).catch((err) => {
console.error('Error connecting to AMI:', err);
setTimeout(startup, 10000); // Retry after 10 seconds
}); });
} }

10
package-lock.json generated
View file

@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"ami": "^1.0.0",
"discord.js": "^14.24.0", "discord.js": "^14.24.0",
"dotenv": "^17.2.3" "dotenv": "^17.2.3"
} }
@ -201,6 +202,15 @@
"npm": ">=7.0.0" "npm": ">=7.0.0"
} }
}, },
"node_modules/ami": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ami/-/ami-1.0.0.tgz",
"integrity": "sha512-wkuxJl8ahQ4FYVwXvtJpQ7wuRUAqD30ueVpeJ1t5UyYqjzh+GtmrnaeHrXzzp+KO3ukhHZoMrsZlKB0j91oAlw==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/discord-api-types": { "node_modules/discord-api-types": {
"version": "0.38.31", "version": "0.38.31",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.31.tgz", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.31.tgz",

View file

@ -9,8 +9,9 @@
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"type": "commonjs", "type": "module",
"dependencies": { "dependencies": {
"ami": "^1.0.0",
"discord.js": "^14.24.0", "discord.js": "^14.24.0",
"dotenv": "^17.2.3" "dotenv": "^17.2.3"
} }