pull/29/head
Matthew 2020-12-02 19:20:05 -05:00
parent 15bd296d67
commit affb9fbde3
No known key found for this signature in database
GPG Key ID: 210AF32ADE3B5C4B
6 changed files with 47 additions and 41 deletions

View File

@ -1,4 +1,7 @@
/* eslint-disable no-continue */ /* eslint-disable no-continue */
import ARIClient from 'ari-client';
import AMIClient from 'asterisk-manager';
import GoogleTTS, { TextToSpeechClient } from '@google-cloud/text-to-speech';
import { Client, Collection, Handler } from '.'; import { Client, Collection, Handler } from '.';
export default class PBX { export default class PBX {
@ -6,27 +9,43 @@ export default class PBX {
public handlers: Collection<Handler>; public handlers: Collection<Handler>;
public ari: ARIClient.Client;
public ami: any;
public tts: TextToSpeechClient;
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.handlers = new Collection<Handler>(); this.handlers = new Collection<Handler>();
this.start(); this.load();
}
private async load() {
this.ari = await ARIClient.connect('http://10.8.0.1:8088/ari', 'cr0', this.client.config.ariClientKey);
this.ari.start(['cr-zero', 'page-dtmf']);
this.ami = new AMIClient(5038, '10.8.0.1', 'cr', this.client.config.amiClientKey);
process.env.GOOGLE_APPLICATION_CREDENTIALS = `${__dirname}/../../google.json`;
this.tts = new GoogleTTS.TextToSpeechClient();
this.load();
} }
public start() { public start() {
this.client.util.ari.on('ChannelHangupRequest', (_, channel) => channel.hangup());
const handlers = Object.values<typeof Handler>(require(`${__dirname}/../pbx`)); const handlers = Object.values<typeof Handler>(require(`${__dirname}/../pbx`));
for (const HandlerFile of handlers) { for (const HandlerFile of handlers) {
const handler = new HandlerFile(this); const handler = new HandlerFile(this);
if (!handler.app) continue; if (!handler.app) continue;
if (!handler.options?.available) { if (!handler.options?.available) {
this.client.util.ari.on('StasisStart', async (event, channel) => { this.ari.on('StasisStart', async (event, channel) => {
if (event.application !== handler.app) return; if (event.application !== handler.app) return;
await handler.unavailable(event, channel); await handler.unavailable(event, channel);
}); });
} else { } else {
this.client.util.ari.on('StasisStart', async (event, channel) => { this.ari.on('StasisStart', async (event, channel) => {
if (event.application !== handler.app) return; if (event.application !== handler.app) return;
try { try {
await handler.run(event, channel); await handler.run(event, channel);

View File

@ -1,10 +1,7 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import GoogleTTS, { TextToSpeechClient } from '@google-cloud/text-to-speech';
import childProcess from 'child_process'; import childProcess from 'child_process';
import { promisify } from 'util'; import { promisify } from 'util';
import ARIClient from 'ari-client';
import AMIClient from 'asterisk-manager';
import signale from 'signale'; import signale from 'signale';
import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel, WebhookPayload } from 'eris'; import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel, WebhookPayload } from 'eris';
import { Client, Command, Moderation, PBX, RichEmbed } from '.'; import { Client, Command, Moderation, PBX, RichEmbed } from '.';
@ -21,12 +18,6 @@ export default class Util {
public pbx: PBX; public pbx: PBX;
public ari: ARIClient.Client;
public ami: any;
public tts: TextToSpeechClient;
constructor(client: Client) { constructor(client: Client) {
this.client = client; this.client = client;
this.moderation = new Moderation(this.client); this.moderation = new Moderation(this.client);
@ -41,19 +32,6 @@ export default class Util {
port: 587, port: 587,
auth: { user: 'internal', pass: this.client.config.emailPass }, auth: { user: 'internal', pass: this.client.config.emailPass },
}); });
this.load();
}
private async load() {
this.ari = await ARIClient.connect('http://10.8.0.1:8088/ari', 'cr0', this.client.config.ariClientKey);
this.ari.start(['cr-zero', 'page-dtmf']);
this.ami = new AMIClient(5038, '10.8.0.1', 'cr', this.client.config.amiClientKey);
process.env.GOOGLE_APPLICATION_CREDENTIALS = `${__dirname}/../../google.json`;
this.tts = new GoogleTTS.TextToSpeechClient();
this.pbx = new PBX(this.client); this.pbx = new PBX(this.client);
} }

View File

@ -20,7 +20,7 @@ export default class Intercom extends Command {
const recordingLocation = await MiscPBXActions.TTS(this.client.util.pbx, `Hello, this is the Library of Code Private Branch Exchange dialing you at the request of ${message.author.username} to deliver you a message. Playing message: ${args.slice(1).join(' ')}`, 'MALE'); const recordingLocation = await MiscPBXActions.TTS(this.client.util.pbx, `Hello, this is the Library of Code Private Branch Exchange dialing you at the request of ${message.author.username} to deliver you a message. Playing message: ${args.slice(1).join(' ')}`, 'MALE');
await loading.edit(`***${this.client.util.emojis.LOADING} Preparing to dial...***`); await loading.edit(`***${this.client.util.emojis.LOADING} Preparing to dial...***`);
this.client.util.ami.action({ this.client.util.pbx.ami.action({
action: 'originate', action: 'originate',
channel: `PJSIP/${args[0]}`, channel: `PJSIP/${args[0]}`,
exten: args[0], exten: args[0],
@ -34,7 +34,7 @@ export default class Intercom extends Command {
'PJSIP_HEADER(add,Alert-Info)': 'Ring Answer', 'PJSIP_HEADER(add,Alert-Info)': 'Ring Answer',
}, },
}, async (err: Error) => { }, async (err: Error) => {
if (err) return loading.edit(`***${this.client.util.emojis.ERROR} Failed to dial extension. | ${err}`); if (err) return loading.edit(`***${this.client.util.emojis.ERROR} Failed to dial extension.***`);
return loading.edit(`***${this.client.util.emojis.SUCCESS} Successfully queued intercom message to EXT \`${args[0]}\`.***`); return loading.edit(`***${this.client.util.emojis.SUCCESS} Successfully queued intercom message to EXT \`${args[0]}\`.***`);
}); });
return undefined; return undefined;

View File

@ -193,7 +193,7 @@ export default class Page extends Command {
const fileExtension = `${randomBytes(10).toString('hex')}`; const fileExtension = `${randomBytes(10).toString('hex')}`;
const [response] = await this.client.util.tts.synthesizeSpeech({ const [response] = await this.client.util.pbx.tts.synthesizeSpeech({
input: { text: `Hello, this call was automatically dialed by the Library of Code Private Branch Exchange. You have received a page from Pager Number, ${senderNumber}. The Pager Code is ${code}. Please check your Direct Messages on Discord for further information.` }, input: { text: `Hello, this call was automatically dialed by the Library of Code Private Branch Exchange. You have received a page from Pager Number, ${senderNumber}. The Pager Code is ${code}. Please check your Direct Messages on Discord for further information.` },
voice: { languageCode: 'en-US', ssmlGender: 'MALE' }, voice: { languageCode: 'en-US', ssmlGender: 'MALE' },
audioConfig: { audioEncoding: 'OGG_OPUS' }, audioConfig: { audioEncoding: 'OGG_OPUS' },
@ -202,7 +202,7 @@ export default class Page extends Command {
await this.client.util.exec(`ffmpeg -i /tmp/${fileExtension}.ogg -af "highpass=f=300, lowpass=f=3400" -ar 8000 -ac 1 -ab 64k -f mulaw /tmp/${fileExtension}.ulaw`); await this.client.util.exec(`ffmpeg -i /tmp/${fileExtension}.ogg -af "highpass=f=300, lowpass=f=3400" -ar 8000 -ac 1 -ab 64k -f mulaw /tmp/${fileExtension}.ulaw`);
try { try {
const chan = await this.client.util.ari.channels.originate({ const chan = await this.client.util.pbx.ari.channels.originate({
endpoint: `PJSIP/${member.extension}`, endpoint: `PJSIP/${member.extension}`,
extension: `${member.extension}`, extension: `${member.extension}`,
callerId: `LOC PBX - PAGE FRM ${senderNumber} <00>`, callerId: `LOC PBX - PAGE FRM ${senderNumber} <00>`,

View File

@ -14,7 +14,7 @@ export default class Misc {
public static async TTS(pbx: PBX, text: string, gender: string | any): Promise<string> { public static async TTS(pbx: PBX, text: string, gender: string | any): Promise<string> {
const fileExtension = `${randomBytes(10).toString('hex')}`; const fileExtension = `${randomBytes(10).toString('hex')}`;
const [response] = await pbx.client.util.tts.synthesizeSpeech({ const [response] = await pbx.tts.synthesizeSpeech({
input: { text }, input: { text },
voice: { languageCode: 'en-US', ssmlGender: gender }, voice: { languageCode: 'en-US', ssmlGender: gender },
audioConfig: { audioEncoding: 'OGG_OPUS' }, audioConfig: { audioEncoding: 'OGG_OPUS' },
@ -23,4 +23,16 @@ export default class Misc {
await pbx.client.util.exec(`ffmpeg -i /tmp/${fileExtension}.ogg -af "highpass=f=300, lowpass=f=3400" -ar 8000 -ac 1 -ab 64k -f mulaw /tmp/${fileExtension}.ulaw`); await pbx.client.util.exec(`ffmpeg -i /tmp/${fileExtension}.ogg -af "highpass=f=300, lowpass=f=3400" -ar 8000 -ac 1 -ab 64k -f mulaw /tmp/${fileExtension}.ulaw`);
return `sound:/tmp/${fileExtension}`; return `sound:/tmp/${fileExtension}`;
} }
public static play(pbx: PBX, channel: ARI.Channel, sound: string): Promise<ARI.Playback> {
const playback = pbx.ari.Playback();
return new Promise((resolve, reject) => {
playback.once('PlaybackFinished', (_, pl: ARI.Playback) => {
resolve(pl);
});
channel.play({ media: sound }, playback).catch((err) => reject(err));
});
}
} }

View File

@ -28,9 +28,10 @@ export default class PageDTMF extends Handler {
media: 'sound:please-enter-the-pn', media: 'sound:please-enter-the-pn',
}, undefined); }, undefined);
channel.on('ChannelDtmfReceived', async (ev) => { channel.on('ChannelDtmfReceived', async (ev) => {
if (!channel.connected) return;
if (status === 0) { if (status === 0) {
if (ev.digit === '#') { if (ev.digit === '#') {
pnPlayback.stop(); await pnPlayback.stop();
await channel.play({ await channel.play({
media: 'sound:please-enter-the-pc', media: 'sound:please-enter-the-pc',
}, undefined); }, undefined);
@ -39,7 +40,7 @@ export default class PageDTMF extends Handler {
} }
pagerNumber.push(ev.digit); pagerNumber.push(ev.digit);
} else if (status === 1) { } else if (status === 1) {
const processingPlayback = this.client.util.ari.Playback(); const processingPlayback = this.pbx.ari.Playback();
if (ev.digit === '#') { if (ev.digit === '#') {
await channel.play({ await channel.play({
media: 'sound:pls-hold-process-tx', media: 'sound:pls-hold-process-tx',
@ -49,10 +50,8 @@ export default class PageDTMF extends Handler {
const page = await Page.page(pagerNumber.join(''), pager.num, pagerCode.join(''), message); const page = await Page.page(pagerNumber.join(''), pager.num, pagerCode.join(''), message);
if (page.status === true) { if (page.status === true) {
processingPlayback.stop(); processingPlayback.stop();
const playback = await channel.play({ await Misc.play(this.pbx, channel, 'sound:page-delivered');
media: 'sound:page-delivered', channel.hangup();
}, undefined);
playback.on('PlaybackFinished', () => channel.hangup());
} else if (page.status === false) { } else if (page.status === false) {
try { try {
const ch = await this.client.getDMChannel(member.userID); const ch = await this.client.getDMChannel(member.userID);
@ -63,10 +62,8 @@ export default class PageDTMF extends Handler {
this.client.util.handleError(new Error(page.message)); this.client.util.handleError(new Error(page.message));
} }
processingPlayback.stop(); processingPlayback.stop();
const errorPlayback = await channel.play({ await Misc.play(this.pbx, channel, 'sound:request-error');
media: 'sound:request-error', channel.hangup();
}, undefined);
errorPlayback.on('PlaybackFinished', () => channel.hangup());
} }
return null; return null;
} }