178 lines
6 KiB
JavaScript
178 lines
6 KiB
JavaScript
require("dotenv").config();
|
|
const fs = require('fs')
|
|
const { execSync } = require('child_process');
|
|
const jsondb = require("node-json-db");
|
|
// Init json db
|
|
var db = new jsondb.JsonDB(new jsondb.Config("known-hosts", true, false, '/', true));
|
|
|
|
const ejs = require("ejs")
|
|
|
|
|
|
const Discord = require("discord.js");
|
|
const { userInfo } = require("os");
|
|
const client = new Discord.Client({intents: "DirectMessages"});
|
|
|
|
|
|
function extractTextFromRawText(rawText) {
|
|
// Strip everything before the line with "[default]". and everything before two newlines, and strip [default]
|
|
const startIndex = rawText.indexOf("[default]");
|
|
const endIndex = rawText.indexOf("\n\n", startIndex);
|
|
|
|
const extractedText = rawText.slice(startIndex, endIndex).replace("[default]\n", "");
|
|
return extractedText;
|
|
}
|
|
|
|
// Split data by newlines, then make key value pairs split by = and make into object
|
|
function parseVoicemailData(data) {
|
|
const lines = data.split("\n");
|
|
const voicemailData = {};
|
|
//console.log(lines)
|
|
lines.forEach((line) => {
|
|
var [key, value] = line.split("=");
|
|
if (!value) return;
|
|
value = value.split(",");
|
|
voicemailData[key] = value;
|
|
});
|
|
|
|
return voicemailData;
|
|
}
|
|
|
|
function getUsers() {
|
|
var vmData = fs.readFileSync(process.env.DEV ? "./dev/voicemail.conf" : "/etc/asterisk/voicemail.conf").toString()
|
|
voicemailData = parseVoicemailData(extractTextFromRawText(vmData));
|
|
return voicemailData;
|
|
}
|
|
|
|
function getEndpoints() {
|
|
if (!process.env.DEV) {
|
|
const currentEndpoint = execSync('asterisk -x "pjsip show endpoints"').toString();
|
|
const endpoints = currentEndpoint.split("\n");
|
|
return endpoints;
|
|
} else {
|
|
return fs.readFileSync("./dev/endpoints.txt").toString().split("\n");
|
|
}
|
|
}
|
|
|
|
function parseEndpointData(data) {
|
|
const endpointRegex = /Endpoint:\s+(\d+\/\d+)\s+([A-Za-z\s]+)\s+(\d+)/g;
|
|
const contactRegex = /Contact:\s*(\d+\/\S+)\s*;?.*\s*(\S+)\s+(\S+)\s+(\d+\.\d+)/g;
|
|
|
|
let endpointObject = {};
|
|
|
|
// Match endpoint details
|
|
const endpointMatch = endpointRegex.exec(data);
|
|
if (endpointMatch) {
|
|
endpointObject.endpoint = endpointMatch[1].split("/")[1].trim(); // e.g. 1000/1000
|
|
endpointObject.state = endpointMatch[2].trim(); // e.g. "Not in use"
|
|
endpointObject.channelCount = parseInt(endpointMatch[3], 10); // e.g. 0
|
|
}
|
|
|
|
if (!endpointObject.endpoint) return undefined;
|
|
|
|
endpointObject.contacts = [];
|
|
|
|
// Match contact details
|
|
let contactMatch;
|
|
while ((contactMatch = contactRegex.exec(data)) !== null) {
|
|
endpointObject.contacts.push({
|
|
host: contactMatch[1].split('@')[1]?.split(";")[0] || "0.0.0.0:0000", // Extract host from the format "sip:1000@ip:port"
|
|
status: contactMatch[3], // "Avail"
|
|
ping: parseFloat(contactMatch[4]), // ping value
|
|
});
|
|
}
|
|
return endpointObject;
|
|
}
|
|
|
|
function parseEndpoints(endpoints) {
|
|
// Ignore first 12 lines
|
|
endpoints = endpoints.slice(12);
|
|
// Endpoints split by double newline
|
|
endpoints = endpoints.join("\n").split("\n\n");
|
|
const users = getUsers();
|
|
const parsedEndpoints = endpoints.map(endpoint => {
|
|
const parsedEndpoint = parseEndpointData(endpoint);
|
|
if (parsedEndpoint && users[parsedEndpoint.endpoint]) {
|
|
parsedEndpoint.discordId = users[parsedEndpoint.endpoint][2];
|
|
parsedEndpoint.name = users[parsedEndpoint.endpoint][1];
|
|
}
|
|
return parsedEndpoint;
|
|
}).filter(endpoint => endpoint !== undefined);
|
|
return parsedEndpoints
|
|
}
|
|
|
|
async function checkForNewEndpoints() {
|
|
const parsed = parseEndpoints(getEndpoints());
|
|
let newContacts = [];
|
|
for (const endpoint of parsed) {
|
|
const seen = await db.getObjectDefault(`/seen/${endpoint.endpoint}`, []);
|
|
const newEndpointContacts = endpoint.contacts.filter(contact => !seen.includes(contact.host.split(":")[0]));
|
|
const uniqueHosts = [...new Set(newEndpointContacts.map(contact => contact.host.split(":")[0]))];
|
|
uniqueHosts.forEach(host => {
|
|
db.push(`/seen/${endpoint.endpoint}[]`, host);
|
|
});
|
|
resp = newEndpointContacts.length > 0 ? {
|
|
endpoint,
|
|
newContacts: newEndpointContacts,
|
|
} : [];
|
|
newContacts = newContacts.concat(resp);
|
|
}
|
|
|
|
return newContacts;
|
|
};
|
|
|
|
function notifyNew() {
|
|
console.log("Checking...")
|
|
checkForNewEndpoints().then(newContacts => {
|
|
if (newContacts.length > 0) {
|
|
newContacts.forEach(async newContact => {
|
|
const { endpoint, newContacts } = newContact;
|
|
const user = await client.users.fetch(endpoint.discordId).catch(() => null);
|
|
if (user) {
|
|
const contactList = newContacts.map(contact => `${contact.host} - ${contact.status} - ${contact.ping}`).join("\n");
|
|
user.send(`New registrations for ${endpoint.endpoint} (${endpoint.name}) from unknown IPs:\n${contactList}`);
|
|
console.log(`Notified ${endpoint.endpoint} (${endpoint.name}) of new contacts`);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
//console.log(Object.fromEntries(Object.entries(getUsers()).map(([key, value]) => [key, value[1]])))
|
|
function renderStatusPage() {
|
|
ejs.renderFile("status.ejs", {endpoints: parseEndpoints(getEndpoints()), userInfo: Object.fromEntries(Object.entries(getUsers()).map(([key, value]) => [key, value[1]]))}, (err, str) => {
|
|
if (err) {
|
|
console.error(err);
|
|
} else {
|
|
fs.writeFileSync(process.env.DEV ? "./dev/index.html" : "/var/www/html/status/index.html", str);
|
|
}
|
|
});
|
|
|
|
const jsonOutput = {
|
|
endpoints: parseEndpoints(getEndpoints()),
|
|
userInfo: Object.fromEntries(Object.entries(getUsers()).map(([key, value]) => [key, value[1]]))
|
|
};
|
|
|
|
jsonOutput.endpoints.forEach(endpoint => {
|
|
const avgPing = endpoint.contacts.reduce((sum, contact) => sum + contact.ping, 0) / endpoint.contacts.length;
|
|
endpoint.contacts = [{ ping: isNaN(avgPing) ? 'No Data' : avgPing.toFixed(2) }];
|
|
});
|
|
|
|
fs.writeFileSync(process.env.DEV ? "./dev/status.json" : "/var/www/html/status/status.json", JSON.stringify(jsonOutput, null, 2));
|
|
}
|
|
|
|
client.on("ready", () => {
|
|
console.log(`Logged in as ${client.user.displayName}!`);
|
|
notifyNew();
|
|
renderStatusPage();
|
|
setInterval(notifyNew, 10*1000);
|
|
setInterval(renderStatusPage, 5*1000);
|
|
});
|
|
|
|
if (process.env.DEV) {
|
|
console.log("DEV")
|
|
renderStatusPage();
|
|
notifyNew();
|
|
} else {
|
|
console.log("PROD")
|
|
client.login(process.env.DISCORD_TOKEN)
|
|
}
|