IT WORKS
This commit is contained in:
commit
34a024e8cf
134
.gitignore
vendored
Normal file
134
.gitignore
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
config.json
|
||||
config.json.old
|
||||
config.json.disabled
|
17
commands.json
Normal file
17
commands.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
[
|
||||
{
|
||||
"name": "whoami",
|
||||
"description": "Get your extension info if you have one",
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"name": "new",
|
||||
"description": "Get an extension on the Telefreunde Community Phone System",
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"name": "list",
|
||||
"description": "List all extensions on the Telefreunde Community Phone System",
|
||||
"type": 1
|
||||
}
|
||||
]
|
73
funcs.js
Normal file
73
funcs.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Some random functions, as to not clutter the main file
|
||||
module.exports = {
|
||||
// Input validation
|
||||
validateInput: function (input, type) {
|
||||
switch (type) {
|
||||
case 'extention':
|
||||
// Check if input is a 3 digit number
|
||||
if (input.length != 3) {
|
||||
return false;
|
||||
}
|
||||
if (isNaN(input)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
},
|
||||
// Generate GraphQL query
|
||||
generateQuery: function (type, args) {
|
||||
switch (type) {
|
||||
case 'lookup':
|
||||
return `query {
|
||||
fetchExtension(extensionId: "${args.ext}") {
|
||||
user {
|
||||
extension
|
||||
name
|
||||
extPassword
|
||||
voicemail
|
||||
}
|
||||
}
|
||||
fetchVoiceMail(extensionId: "${args.ext}") {
|
||||
password
|
||||
email
|
||||
}
|
||||
}`
|
||||
break;
|
||||
case 'list':
|
||||
return `query {
|
||||
fetchAllExtensions {
|
||||
extension {
|
||||
user {
|
||||
extension
|
||||
name
|
||||
voicemail
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
break;
|
||||
case 'add':
|
||||
return `mutation {
|
||||
addExtension(input: {
|
||||
extensionId: "${args.ext}"
|
||||
name: "${args.name}"
|
||||
email: "${args.uid}"
|
||||
vmEnable: true
|
||||
vmPassword: "${args.ext}"
|
||||
}) {
|
||||
status
|
||||
}
|
||||
}`;
|
||||
break;
|
||||
case 'reload':
|
||||
return `mutation {
|
||||
doreload(input: {clientMutationId: "${args.id}"}) {
|
||||
status
|
||||
}
|
||||
}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
289
index.js
Normal file
289
index.js
Normal file
|
@ -0,0 +1,289 @@
|
|||
//Load static files
|
||||
const config = require("./config.json");
|
||||
const funcs = require("./funcs.js");
|
||||
|
||||
// FreePBX GraphQL Client
|
||||
const {
|
||||
FreepbxGqlClient,
|
||||
gql
|
||||
} = require("freepbx-graphql-client");
|
||||
console.log(config.freepbx.clientid)
|
||||
const pbxClient = new FreepbxGqlClient(config.freepbx.url, {
|
||||
client: {
|
||||
id: config.freepbx.clientid,
|
||||
secret: config.freepbx.secret,
|
||||
}
|
||||
});
|
||||
|
||||
// Some functions for FreePBX
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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"}}]}}
|
||||
result.fetchAllExtensions.extension.forEach((ext) => {
|
||||
if (ext.user.extension > highest) {
|
||||
highest = ext.user.extension;
|
||||
}
|
||||
});
|
||||
// Return the next extension
|
||||
res = {
|
||||
"status": "success",
|
||||
"result": String(Number(highest) + 1)
|
||||
}
|
||||
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);
|
||||
|
||||
dcClient.on('ready', () => {
|
||||
console.log(`Logged in as ${dcClient.user.tag}!`);
|
||||
// Set up application commands
|
||||
const commands = require('./commands.json');
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(dcClient.user.id, config.discord.guildId), {
|
||||
body: commands
|
||||
}
|
||||
);
|
||||
console.log('Successfully reloaded application (/) commands.');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
||||
|
||||
});
|
||||
|
||||
dcClient.on('interactionCreate', async interaction => {
|
||||
if (!interaction.isCommand()) return;
|
||||
if (interaction.user.id != config.discord.devId) return; // Only allow the dev to use this bot (for now)
|
||||
const {
|
||||
commandName
|
||||
} = interaction;
|
||||
switch (commandName) {
|
||||
case "new":
|
||||
interaction.reply({
|
||||
content: "Please Wait...",
|
||||
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}...`)
|
||||
console.log(`Creating extension ${ext} for ${name} (${uid})`)
|
||||
// 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
|
||||
}
|
||||
]
|
||||
}]
|
||||
})
|
||||
// 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.reply(`Error creating extension: ${error}`);
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
interaction.reply(`Error finding next available extension: ${error}`);
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "whoami":
|
||||
interaction.reply({ content: "Please Wait...", 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
|
||||
console.log(error)
|
||||
interaction.reply({
|
||||
content: "You don't have an extension!",
|
||||
ephemeral: true
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case "list":
|
||||
interaction.reply({
|
||||
content: "Not Implemented Yet",
|
||||
ephemeral: true
|
||||
})
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
dcClient.login(config.discord.token);
|
2651
package-lock.json
generated
Normal file
2651
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
16
package.json
Normal file
16
package.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "freepbx-manager",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.7.1",
|
||||
"freepbx-graphql-client": "^0.1.1",
|
||||
"sqlite3": "^5.1.4"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue