discord-freepbx-manager/index.js

748 lines
22 KiB
JavaScript

//Load static files
const config = require("./config.json");
const funcs = require("./funcs.js");
const colors = require("colors");
// FreePBX GraphQL Client
const {
FreepbxGqlClient,
gql
} = require("freepbx-graphql-client");
const pbxClient = new FreepbxGqlClient(config.freepbx.url, {
client: {
id: config.freepbx.clientid,
secret: config.freepbx.secret,
}
});
// Some functions for FreePBX
const getExtCount = () => {
return new Promise((resolve, reject) => {
pbxClient.request(funcs.generateQuery('list', {})).then((result) => {
resolve(result.fetchAllExtensions.extension.length);
}).catch((error) => {
reject(error);
});
});
}
const createExtension = (ext, name, uid) => {
return new Promise((resolve, reject) => {
pbxClient.request(funcs.generateQuery('lookup', {
ext: ext
})).then((result) => {
// Extension exists
res = {
"status": "exists",
}
resolve(res);
}).catch((error) => {
// Extension does not exist, create it, reload, look it up, and return the result
pbxClient.request(funcs.generateQuery('add', {
ext: ext,
name: name,
uid: uid
})).then((result) => {
pbxClient.request(funcs.generateQuery('reload', {
id: "CreateExt"
})).then((result) => {
pbxClient.request(funcs.generateQuery('lookup', {
ext: ext
})).then((result) => {
res = {
"status": "created",
"result": result
}
resolve(res);
}).catch((error) => {
reject(error);
});
}).catch((error) => {
reject(error);
});
}).catch((error) => {
reject(error);
});
});
});
}
// deleteExtension, takes an extension number
const deleteExtension = (ext) => {
return new Promise((resolve, reject) => {
pbxClient.request(funcs.generateQuery('delete', {
ext: ext
})).then((result) => {
pbxClient.request(funcs.generateQuery('reload', {
id: "DeleteExt"
})).then((result) => {
res = {
"status": "deleted",
"result": result
}
resolve(res);
}).catch((error) => {
reject(error);
});
}).catch((error) => {
reject(error);
});
});
}
const lookupExtension = (ident, type) => { // type is either "ext" or "uid"
return new Promise((resolve, reject) => {
switch (type) {
case "ext":
pbxClient.request(funcs.generateQuery('lookup', {
ext: ident
})).then((result) => {
res = {
"status": "exists",
"result": result
}
resolve(res);
}).catch((error) => {
res = {
"status": "notfound",
"result": error
}
reject(res);
});
break;
case "uid":
// Find the extension based on Discord ID in the voicemail email field
pbxClient.request(funcs.generateQuery('list', {})).then(async (result) => {
// loop through all extensions, run a lookup on each one, and return the first one that matches
var found = false;
var ext = "";
var count = 0;
result.fetchAllExtensions.extension.forEach(async (ext) => {
pbxClient.request(funcs.generateQuery('lookup', {
ext: ext.user.extension
})).then((result) => {
if (result.fetchVoiceMail.email == ident && !found) {
found = true;
ext = result;
clearInterval(x);
resolve({
"status": "exists",
"result": ext
})
}
count++;
}).catch((error) => {
reject(error);
});
});
x = setInterval(() => {
if (count == result.fetchAllExtensions.extension.length) {
clearInterval(x);
if (!found) {
reject("Not found");
}
}
}, 100);
}).catch((error) => {
reject(error);
});
break;
default:
reject("Invalid type");
}
});
}
const findNextExtension = () => {
return new Promise((resolve, reject) => {
pbxClient.request(funcs.generateQuery('list', {})).then((result) => {
// Find the highest extension
var highest = 0;
// output looks like {fetchAllExtensions: { extension: [{user:{extension: 100, name: "Test"}}]}}
// Look out for gaps in the extension numbers, if there are any, use that one, if not, use the highest + 1
var exts = [];
result.fetchAllExtensions.extension.forEach((ext) => {
exts.push(Number(ext.user.extension));
});
exts.sort((a, b) => a - b);
// Start should be the lowest extension. If none exists use config value
// Await if statement
var start = 0;
if (exts.length > 0) {
start = exts[0];
} else {
start = config.freepbx.startExt;
exts[0] = start - 1;
}
for (var i = 0; i < exts.length; i++) {
if (exts[i] != i + config.freepbx.startExt) {
highest = i + start;
break;
}
}
if (highest == 0) {
highest = String(Number(exts[exts.length - 1]) + 1);
}
// Return the next extension
res = {
"status": "success",
"result": String(highest)
}
resolve(res);
}).catch((error) => {
reject(error);
});
});
}
// Load Discord.js
const Discord = require("discord.js");
const {
REST,
Routes
} = require('discord.js');
const dcClient = new Discord.Client({
intents: ["Guilds", "GuildMembers"]
});
const rest = new REST({
version: '10'
}).setToken(config.discord.token);
var logChannel;
var sendLog;
dcClient.on('ready', async () => {
await dcClient.channels.fetch(config.discord.logId).then((channel) => {
sendLog = (message) => {
//channel.send(`\`\`\`ansi\n${message}\`\`\``);
console.log(message);
};
sendLog(`${colors.cyan("[INFO]")} Logged in as ${dcClient.user.tag}!`);
// Set up application commands
const commands = require('./commands.json');
(async () => {
try {
sendLog(`${colors.cyan("[INFO]")} Started refreshing application (/) commands.`);
await rest.put(
Routes.applicationGuildCommands(dcClient.user.id, config.discord.guildId), {
body: commands
}
);
sendLog(`${colors.cyan("[INFO]")} Successfully reloaded application (/) commands.`);
} catch (error) {
console.error(`${colors.red("[ERROR]")} ${error}`);
}
})();
// Presence Stuff
getExtCount().then((result) => {
dcClient.user.setPresence({
activities: [{
name: `${result} extensions`,
type: "WATCHING"
}],
status: "online"
});
}).catch((error) => {
sendLog(`${colors.red("[ERROR]")} ${error}`);
});
// Run every 5 minutes
setInterval(() => {
getExtCount().then((result) => {
dcClient.user.setPresence({
activities: [{
name: `${result} extensions`,
type: "WATCHING"
}],
status: "online"
});
}).catch((error) => {
sendLog(`${colors.red("[ERROR]")} ${error}`);
});
}, 300000);
// Lookup all extensions and check if they're still in the server
// If they're not, delete them
// Run once on startup
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
extensions.forEach((extension) => {
lookupExtension(extension.user.extension, "ext").then((result) => {
if (result.result.fetchVoiceMail.email == null) {
// Extension is not part of the bot, do nothing
return;
};
// Fetch Discord user using ID stored in result.result.fetchVoiceMail.email, and see if they're in the server
dcClient.guilds.cache.get(config.discord.guildId).members.fetch(result.result.fetchVoiceMail.email).then((member) => {
// They're in the server, do nothing
}).catch((error) => {
// They're not in the server, delete the extension
sendLog(`${colors.cyan("[INFO]")} ${extension.user.extension} is not in the server, deleting it`);
deleteExtension(extension.user.extension).then((result) => {
sendLog(`${colors.cyan("[INFO]")} Deleted extension ${extension.user.extension} because the user is no longer in the server`);
}).catch((error) => {
sendLog(`${colors.red("[ERROR]")} ${error}`);
});
});
});
});
})
// Run every 5 minutes
const extListChannel = dcClient.channels.cache.get(config.discord.extList);
// Find the latest message from the bot in extListChannel, if there isn't one, send one. There can be other messages in the channel
// Sends the same message as the list command
setInterval(async () => {
await extListChannel.messages.fetch({
limit: 1
}).then((messages) => {
if (messages.size == 0) {
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
// key:value pairs of extension:username
let extensionList = {};
extensions.forEach((extension) => {
extensionList[extension.user.extension] = extension.user.name;
});
extensionList1 = "";
for (let key in extensionList) {
extensionList1 += `${key}: ${extensionList[key]}\n`;
}
extListChannel.send({
content: "",
embeds: [{
"title": "Extension List",
"color": 0x00ff00,
"description": `${extensionList1}`
}]
});
})
} else {
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
// key:value pairs of extension:username
let extensionList = {};
extensions.forEach((extension) => {
extensionList[extension.user.extension] = extension.user.name;
});
extensionList1 = "";
for (let key in extensionList) {
extensionList1 += `${key}: ${extensionList[key]}\n`;
}
messages.first().edit({
content: "",
embeds: [{
"title": "Extension List",
"color": 0x00ff00,
"description": `${extensionList1}`
}]
});
})
}
})
}, 300000);
// Also run on startup
extListChannel.messages.fetch({
limit: 1
}).then((messages) => {
if (messages.size == 0) {
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
// key:value pairs of extension:username
let extensionList = {};
extensions.forEach((extension) => {
extensionList[extension.user.extension] = extension.user.name;
});
extensionList1 = "";
for (let key in extensionList) {
extensionList1 += `${key}: ${extensionList[key]}\n`;
}
extListChannel.send({
content: "",
embeds: [{
"title": "Extension List",
"color": 0x00ff00,
"description": `${extensionList1}`
}]
});
})
} else {
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
// key:value pairs of extension:username
let extensionList = {};
extensions.forEach((extension) => {
extensionList[extension.user.extension] = extension.user.name;
});
extensionList1 = "";
for (let key in extensionList) {
extensionList1 += `${key}: ${extensionList[key]}\n`;
}
messages.first().edit({
content: "",
embeds: [{
"title": "Extension List",
"color": 0x00ff00,
"description": `${extensionList1}`
}]
});
})
}
})
});
// Automatically create extensions for all existing members that don't have one
// Temporarily disabled, until I can fix it.
// dcClient.guilds.fetch(config.discord.guildId).then((guild) => {
// console.log(guild.name)
// let waitR = 0;
// let waitW = 0;
// y = guild.members.fetch().then((members) => {
// clearInterval(x)
// console.log(members.size)
// members.forEach((member) => {
// if (member.user.bot) return;
// setTimeout(() => {
// console.log(`Checking ${member.user.tag}`)
// timeoutA = setTimeout(() => {
// findNextExtension().then((result) => {
// let uid = member.user.id;
// let ext = result.result;
// let name = member.user.tag;
// createExtension(ext, name, uid).then((result) => {
// sendLog(`${colors.cyan("[INFO]")} Created extension ${result.result.createExtension.extension} for ${member.user.tag}`);
// }).catch((error) => {
// sendLog(`${colors.red("[ERROR]")} ${member.user.tag} ${error}`);
// });
// })
// }, 10000) // 10 second timeout, assume no extension exists
// lookupExtension(member.id, "uid").then((result) => {
// clearTimeout(timeoutA);
// if (result.status == "exists") {
// // Extension already exists, do nothing
// } else {
// console.log()
// findNextExtension().then((result) => {
// let uid = member.user.id;
// let ext = result.result;
// let name = member.user.tag;
// createExtension(ext, name, uid).then((result) => {
// sendLog(`${colors.cyan("[INFO]")} Created extension ${result.result.createExtension.extension} for ${member.user.tag}`);
// }).catch((error) => {
// sendLog(`${colors.red("[ERROR]")} ${member.user.tag} ${error}`);
// });
// })
// }
// }).catch((error) => {
// clearTimeout(timeoutA);
// console.log(error)
// findNextExtension().then((result) => {
// let uid = member.user.id;
// let ext = result.result;
// let name = member.user.tag;
// createExtension(ext, name, uid).then((result) => {
// sendLog(`${colors.cyan("[INFO]")} Created extension ${result.result.createExtension.extension} for ${member.user.tag}`);
// }).catch((error) => {
// sendLog(`${colors.red("[ERROR]")} ${member.user.tag} ${error}`);
// });
// })
// console.log(`Creating extension for ${member.user.tag} in ${waitW} seconds`)
// });
// }, waitR * 1000)
// console.log(`Waiting ${waitR} seconds to check ${member.user.tag}`)
// waitR+=2;
// });
// }).catch((error) => {
// sendLog(`${colors.red("[ERROR]")} ${error}`);
// });
// x = setInterval(() => {
// console.log(y)
// }, 1000)
// });
});
dcClient.on("guildMemberRemove", (member) => {
// Delete the extension if the user leaves the server
sendLog(`${colors.cyan("[INFO]")} User ${member.id} left the server`)
lookupExtension(member.id, "uid").then((result) => {
if (result.status == "exists") {
sendLog(`${colors.cyan("[INFO]")} User ${member.id} has extension ${result.result.fetchVoiceMail.extension}, deleting it`)
deleteExtension(result.result.fetchVoiceMail.extension).then((result) => {
sendLog(`${colors.cyan("[INFO]")} Deleted extension ${result.result.fetchVoiceMail.extension} because the user left the server`);
}).catch((error) => {
sendLog(`${colors.red("[ERROR]")} ${error}`);
});
}
}).catch((error) => {
sendLog(`${colors.red("[ERROR]")} ${error}`);
});
});
dcClient.on('interactionCreate', async interaction => {
if (interaction.isCommand()) {
const {
commandName
} = interaction;
switch (commandName) {
case "new":
await interaction.deferReply({
ephemeral: true
});
lookupExtension(interaction.user.id, "uid").then((result) => {
if (result.status == "exists") {
// The user already has an extension, return an ephemeral message saying so
interaction.editReply({
content: "You already have an extension!",
ephemeral: true
});
}
}).catch((error) => {
// The user doesn't have an extension, create one
findNextExtension().then((result) => {
if (result.status == "success") {
let uid = interaction.user.id;
let ext = result.result;
let name = interaction.user.tag;
interaction.editReply(`Creating extension ${ext}...`)
// Create the extension
createExtension(ext, name, uid).then((result) => {
if (result.status == "created") {
interaction.editReply({
content: "",
embeds: [{
"title": "Extension Created!",
"color": 0x00ff00,
"description": `The SIP server is \`${config.freepbx.server}\``,
"fields": [{
"name": "Extension/Username",
"value": ext
},
{
"name": "Password",
"value": `||${result.result.fetchExtension.user.extPassword}||`
}
]
}]
})
sendLog(`${colors.cyan("[INFO]")} Created extension ${ext} for user ${uid}`);
// Add the role to the user on Discord based on the ID in the config file
let role = interaction.guild.roles.cache.find(role => role.id === config.discord.roleId);
interaction.member.roles.add(role);
}
}).catch((error) => {
interaction.editReply(`Error creating extension: ${error}`);
});
}
}).catch((error) => {
interaction.editReply(`Error finding next available extension: ${error}`);
});
});
break;
case "whoami":
await interaction.deferReply({
ephemeral: true
});
lookupExtension(interaction.user.id, "uid").then((result) => {
if (result.status == "exists") {
// The user already has an extension, return an ephemeral message saying so
interaction.editReply({
content: "",
embeds: [{
"title": "Extension Info",
"color": 0x00ff00,
"description": `The SIP server is \`${config.freepbx.server}\``,
"fields": [{
"name": "Extension/Username",
"value": result.result.fetchExtension.user.extension
},
{
"name": "Password",
"value": `||${result.result.fetchExtension.user.extPassword}||`
}
]
}],
ephemeral: true
})
}
}).catch((error) => {
// The user doesn't have an extension, create one
sendLog(`${colors.red("[ERROR]")} ${error}`)
interaction.editReply({
content: "You don't have an extension!",
ephemeral: true
});
});
break;
case "list":
await interaction.deferReply({
ephemeral: false
});
pbxClient.request(funcs.generateQuery("list", {})).then((result) => {
let extensions = result.fetchAllExtensions.extension;
// key:value pairs of extension:username
let extensionList = {};
extensions.forEach((extension) => {
extensionList[extension.user.extension] = extension.user.name;
});
extensionList1 = "";
for (let key in extensionList) {
extensionList1 += `${key}: ${extensionList[key]}\n`;
}
interaction.editReply({
content: "",
embeds: [{
"title": "Extension List",
"color": 0x00ff00,
"description": `${extensionList1}`
}]
});
}).catch((error) => {
interaction.editReply(`Error listing extensions: ${error}`);
});
break;
case "delete":
if (interaction.options.get("confirm").value == false) {
interaction.reply({
content: "Please confirm you want to delete your extension by running `/delete confirm:true`",
ephemeral: true
})
break;
}
await interaction.deferReply({
ephemeral: true
});
lookupExtension(interaction.user.id, "uid").then((result) => {
if (result.status == "exists") {
// The user has an extension, delete it
deleteExtension(result.result.fetchExtension.user.extension).then((result) => {
if (result.status == "deleted") {
interaction.editReply({
content: "Extension Deleted!",
ephemeral: true
});
sendLog(`${colors.green("[INFO]")} ${interaction.user.tag} (${interaction.user.id}) deleted extension ${result.result.fetchExtension.user.extension}`)
// Remove the role from the user on Discord based on the ID in the config file
let role = interaction.guild.roles.cache.find(role => role.id === config.discord.roleId);
interaction.member.roles.remove(role);
}
}).catch((error) => {
interaction.reply(`Error deleting extension: ${error}`);
});
}
}).catch((error) => {
// The user doesn't have an extension, return an ephemeral message saying so
interaction.editReply({
content: "You don't have an extension!",
ephemeral: true
});
});
break;
case "button":
interaction.channel.send({
embeds: [
{
"title": "Community Phone System",
"description": "Click the button below to get your extension!",
"color": null
}
],
components: [
{
type: 1,
components: [
{
type: 2,
label: "Get an Extension",
emoji: {
name: "✅"
},
style: 3,
custom_id: "new"
}
]
}
]
}).then(() => {
interaction.reply({
content: "Button sent!",
ephemeral: true
})
});
default:
break;
}
}
if (interaction.isButton()) {
switch (interaction.customId) {
case "new":
await interaction.deferReply({
ephemeral: true
});
lookupExtension(interaction.user.id, "uid").then((result) => {
if (result.status == "exists") {
// The user already has an extension, return an ephemeral message saying so
interaction.editReply({
content: "You already have an extension!",
ephemeral: true
});
}
}).catch((error) => {
// The user doesn't have an extension, create one
findNextExtension().then((result) => {
if (result.status == "success") {
let uid = interaction.user.id;
let ext = result.result;
let name = interaction.user.tag;
interaction.editReply(`Creating extension ${ext}...`)
// Create the extension
createExtension(ext, name, uid).then((result) => {
if (result.status == "created") {
interaction.editReply({
content: "",
embeds: [{
"title": "Extension Created!",
"color": 0x00ff00,
"description": `The SIP server is \`${config.freepbx.server}\``,
"fields": [{
"name": "Extension/Username",
"value": ext
},
{
"name": "Password",
"value": `||${result.result.fetchExtension.user.extPassword}||`
}
]
}]
})
sendLog(`${colors.cyan("[INFO]")} Created extension ${ext} for user ${uid}`);
// Add the role to the user on Discord based on the ID in the config file
let role = interaction.guild.roles.cache.find(role => role.id === config.discord.roleId);
interaction.member.roles.add(role);
}
}).catch((error) => {
interaction.editReply(`Error creating extension: ${error}`);
});
}
}).catch((error) => {
interaction.editReply(`Error finding next available extension: ${error}`);
});
});
break;
}
}
});
dcClient.login(config.discord.token);