diff --git a/bin/ari-transcriber b/bin/ari-transcriber index 9e51657..af09248 100755 --- a/bin/ari-transcriber +++ b/bin/ari-transcriber @@ -32,7 +32,14 @@ require(`yargs`) (yargs) => { yargs.example("$0 --help --format=slin16 --sslCert=/etc/letsencrypt/live/myserver/fullchain.pem --sslKey=/etc/letsencrypt/live/myserver/privkey.pem --wssPort=39990 --speakerDiarization 'Local/1170' ") yargs.wrap(yargs.terminalWidth()); - yargs.positional('dialstring', {describe: 'Extension to dial such as "Local/1234"'}); + yargs.parserConfiguration({ + "parse-numbers": false, + "parse-positional-numbers": false + }); + yargs.positional('dialstring', { + describe: 'Extension to dial such as "Local/1234" (or with -s, what to snoop on eg. "PJSIP/1234-0000abcd" or "1716337601.107")', + type: 'string' + }); }, opts => { new transcriber.AriTranscriber(opts); @@ -140,6 +147,13 @@ require(`yargs`) description: "WebSocket server port. If omitted, no websocket server will be started", group: "Transcription WebSocket", type: 'number', + }, + snoopTarget: { + alias: 's', + global: true, + requiresArg: false, + description: "Use as the unique ID or channel name to snoop on", + type: 'boolean', } }) .strict().argv; diff --git a/lib/ari-controller.js b/lib/ari-controller.js index 4e88f70..d68f879 100644 --- a/lib/ari-controller.js +++ b/lib/ari-controller.js @@ -39,27 +39,39 @@ class AriController extends EventEmitter { } this.closing = true; + if (this.dogChannelId) { + console.log("Hanging up dog channel id %s", this.dogChannelId); + try { + await this.ari.channels.hangup({channelId: this.dogChannelId}); + } catch(error) { + console.error("Issue hanging up dog channel %s", error.message); + } + delete this.dogChannelId; + } if (this.localChannel) { - console.log("Hanging up local channel"); + console.log("Hanging up local channel %s", this.localChannel.id); try { await this.localChannel.hangup(); } catch(error) { + console.error("Issue hanging up local channel %s", error.message); } delete this.localChannel; } if (this.externalChannel) { - console.log("Hanging up external media channel"); + console.log("Hanging up external media channel %s", this.externalChannel.id); try { await this.externalChannel.hangup(); } catch(error) { + console.error("Issue hanging up external media channel %s", error.message); } delete this.externalChannel; } if (this.bridge) { - console.log("Destroying bridge"); + console.log("Destroying bridge %s", this.bridge.name); try { await this.bridge.destroy(); } catch(error) { + console.error("Issue destroying bridge %s", error.message); } delete this.bridge; } @@ -76,6 +88,7 @@ class AriController extends EventEmitter { this.options.ariServerUrl, this.options.ariUser, this.options.ariPassword); await this.ari.start("externalMedia"); + await this.ari.start("snoopLeg"); // Create a simple bridge that is controlled by ARI/Stasis this.bridge = this.ari.Bridge(); @@ -86,6 +99,7 @@ class AriController extends EventEmitter { this.close(); } this.bridge.on('BridgeDestroyed', (event) => { + console.log("Bridge Destroyed"); this.close(); }); @@ -106,13 +120,62 @@ class AriController extends EventEmitter { this.close(); }); - // Call the phone or confbridge specified in dialstring - try { - await this.localChannel.originate({ - endpoint: this.options.dialstring, formats: this.options.format, app: "externalMedia", + if (this.options.snoopTarget) { + + /* + * Hmm, would be nice if snoopChannel co-operated, eg. + * this.dogChannel = this.ari.Channel(); + * await this.dogChannel.snoopChannel(...) + * Until then, carry around the dog channel ID instead! + */ + + /* global callback to parse out the bridge id */ + this.ari.on('StasisStart', (event, chan) => { + let app_data = chan.dialplan.app_data.split(","); + if (app_data[0] == "snoopLeg") { + if (app_data[1] == this.bridge.id) { + this.dogChannelId = chan.id; + this.bridge.addChannel({channel: chan.id}); + console.log("snoopLeg connected channel '%s' bridge '%s'", chan.id, this.bridge.id); + } + } }); - } catch (error) { - this.close(); + + /* global callback to hangup when dogChannel dies */ + this.ari.on('StasisEnd', (event, chan) => { + let app_data = chan.dialplan.app_data.split(","); + if (app_data[0] == "snoopLeg") { + if (app_data[1] == this.bridge.id) { + console.log("snoopLeg disconnected channel '%s' bridge '%s'", chan.id, this.bridge.id); + this.close(); + } + } + }); + + /* register the bridge id started above as parameter to stasis app */ + try { + await this.ari.channels.snoopChannel({ + app: "snoopLeg", + appArgs: this.bridge.id, + channelId: this.options.dialstring, + spy: "both", + whisper: "none" + }); + } catch (error) { + console.error("Could not snoop on '%s'", this.options.dialstring); + this.close(); + } + + } else { + + // Call the phone or confbridge specified in dialstring + try { + await this.localChannel.originate({ + endpoint: this.options.dialstring, formats: this.options.format, app: "externalMedia", + }); + } catch (error) { + this.close(); + } } // Now we create the External Media channel. @@ -141,4 +204,4 @@ class AriController extends EventEmitter { } } -module.exports.AriController = AriController; \ No newline at end of file +module.exports.AriController = AriController;