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