diff --git a/src/class/PBX.ts b/src/class/PBX.ts index 03ca259..03c0230 100644 --- a/src/class/PBX.ts +++ b/src/class/PBX.ts @@ -1,4 +1,7 @@ /* 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 '.'; export default class PBX { @@ -6,27 +9,43 @@ export default class PBX { public handlers: Collection; + public ari: ARIClient.Client; + + public ami: any; + + public tts: TextToSpeechClient; + constructor(client: Client) { this.client = client; this.handlers = new Collection(); - 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() { - this.client.util.ari.on('ChannelHangupRequest', (_, channel) => channel.hangup()); const handlers = Object.values(require(`${__dirname}/../pbx`)); for (const HandlerFile of handlers) { const handler = new HandlerFile(this); if (!handler.app) continue; 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; await handler.unavailable(event, channel); }); } else { - this.client.util.ari.on('StasisStart', async (event, channel) => { + this.ari.on('StasisStart', async (event, channel) => { if (event.application !== handler.app) return; try { await handler.run(event, channel); diff --git a/src/class/Util.ts b/src/class/Util.ts index 04b7627..dab5ff3 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -1,10 +1,7 @@ /* eslint-disable no-bitwise */ import nodemailer from 'nodemailer'; -import GoogleTTS, { TextToSpeechClient } from '@google-cloud/text-to-speech'; import childProcess from 'child_process'; import { promisify } from 'util'; -import ARIClient from 'ari-client'; -import AMIClient from 'asterisk-manager'; import signale from 'signale'; import { Member, Message, Guild, PrivateChannel, GroupChannel, Role, AnyGuildChannel, WebhookPayload } from 'eris'; import { Client, Command, Moderation, PBX, RichEmbed } from '.'; @@ -21,12 +18,6 @@ export default class Util { public pbx: PBX; - public ari: ARIClient.Client; - - public ami: any; - - public tts: TextToSpeechClient; - constructor(client: Client) { this.client = client; this.moderation = new Moderation(this.client); @@ -41,19 +32,6 @@ export default class Util { port: 587, 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); } diff --git a/src/commands/intercom.ts b/src/commands/intercom.ts index 0bbdbec..a095326 100644 --- a/src/commands/intercom.ts +++ b/src/commands/intercom.ts @@ -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'); await loading.edit(`***${this.client.util.emojis.LOADING} Preparing to dial...***`); - this.client.util.ami.action({ + this.client.util.pbx.ami.action({ action: 'originate', channel: `PJSIP/${args[0]}`, exten: args[0], @@ -34,7 +34,7 @@ export default class Intercom extends Command { 'PJSIP_HEADER(add,Alert-Info)': 'Ring Answer', }, }, 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 undefined; diff --git a/src/commands/page.ts b/src/commands/page.ts index 7243a29..0f09386 100644 --- a/src/commands/page.ts +++ b/src/commands/page.ts @@ -193,7 +193,7 @@ export default class Page extends Command { 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.` }, voice: { languageCode: 'en-US', ssmlGender: 'MALE' }, 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`); try { - const chan = await this.client.util.ari.channels.originate({ + const chan = await this.client.util.pbx.ari.channels.originate({ endpoint: `PJSIP/${member.extension}`, extension: `${member.extension}`, callerId: `LOC PBX - PAGE FRM ${senderNumber} <00>`, diff --git a/src/pbx/actions/Misc.ts b/src/pbx/actions/Misc.ts index 3c578ad..2465a9d 100644 --- a/src/pbx/actions/Misc.ts +++ b/src/pbx/actions/Misc.ts @@ -14,7 +14,7 @@ export default class Misc { public static async TTS(pbx: PBX, text: string, gender: string | any): Promise { const fileExtension = `${randomBytes(10).toString('hex')}`; - const [response] = await pbx.client.util.tts.synthesizeSpeech({ + const [response] = await pbx.tts.synthesizeSpeech({ input: { text }, voice: { languageCode: 'en-US', ssmlGender: gender }, 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`); return `sound:/tmp/${fileExtension}`; } + + public static play(pbx: PBX, channel: ARI.Channel, sound: string): Promise { + 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)); + }); + } } diff --git a/src/pbx/handlers/PageDTMF.ts b/src/pbx/handlers/PageDTMF.ts index 31c8aaa..c373eba 100644 --- a/src/pbx/handlers/PageDTMF.ts +++ b/src/pbx/handlers/PageDTMF.ts @@ -28,9 +28,10 @@ export default class PageDTMF extends Handler { media: 'sound:please-enter-the-pn', }, undefined); channel.on('ChannelDtmfReceived', async (ev) => { + if (!channel.connected) return; if (status === 0) { if (ev.digit === '#') { - pnPlayback.stop(); + await pnPlayback.stop(); await channel.play({ media: 'sound:please-enter-the-pc', }, undefined); @@ -39,7 +40,7 @@ export default class PageDTMF extends Handler { } pagerNumber.push(ev.digit); } else if (status === 1) { - const processingPlayback = this.client.util.ari.Playback(); + const processingPlayback = this.pbx.ari.Playback(); if (ev.digit === '#') { await channel.play({ 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); if (page.status === true) { processingPlayback.stop(); - const playback = await channel.play({ - media: 'sound:page-delivered', - }, undefined); - playback.on('PlaybackFinished', () => channel.hangup()); + await Misc.play(this.pbx, channel, 'sound:page-delivered'); + channel.hangup(); } else if (page.status === false) { try { 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)); } processingPlayback.stop(); - const errorPlayback = await channel.play({ - media: 'sound:request-error', - }, undefined); - errorPlayback.on('PlaybackFinished', () => channel.hangup()); + await Misc.play(this.pbx, channel, 'sound:request-error'); + channel.hangup(); } return null; }