Author Topic: Yate outgoing call connected to javascript IVR  (Read 6332 times)

asymetrixs

  • Administrator
  • Newbie
  • *****
  • Posts: 47
    • View Profile
Yate outgoing call connected to javascript IVR
« on: March 21, 2018, 02:22:00 PM »
Hi,

I want yate to make an outgoing call to a party and once this party answers the call, it should be connected to an internal javascript that runs an IVR.

I know that via javascript routing I can connect an incoming call to a javascript-ivr, but in this scenario it is an outgoing call.

What I have so far is a javascript registered in javascript.conf
Code: [Select]
jstrigger=trigger.js
with the following content, which causes Yate to create an outgoing call (in this case to my SIP phone which address is in m.getResult(0,1)).
Now after establishing this call I want to connect it to a javascript that plays a sound file and can handle DTMF. The problem right now is connecting it to this file. Once it is connected, I guess I can simply install handlers for chan.dtmf and receive the buttons pressed.

Code: [Select]
function onTimer(msg)
{
    //Engine.debug(Engine.DebugInfo,"Got Engine.Timer");

    // Get DB Object
    var m = new Message("database");
    // Specify connection to use
    m.account = "ivrcon";
    // Define Query
    m.query = "SELECT OK, number, call FROM get_next_call();";
    // Run the Query
    if (m.dispatch())
    {
        if (m.rows > 0)
        {
            Engine.debug(Engine.DebugInfo, "Got " + m.rows + " records of " + m.columns + " fields");

            var executeMsg = new Message("call.execute", msg);
    executeMsg.id = Math.random(10000,10000000);
            //executeMsg.callto = "wave/play//usr/share/yate/sounds/prompt_for_destination.alaw";
            executeMsg.callto = "wave/play/-"; // silence
            executeMsg.called = m.getResult(0,1);
            executeMsg.caller = "Hans";
            executeMsg.callername = "Hans";
            executeMsg.dispatch();
        }
    }
    else
    {
        Engine.output("Query failed");
    }

    return false;
}

Engine.debugName("ivr-call-trigger");
Message.trackName("ivr-call-trigger");
Engine.debugEnabled(true);
Message.install(onTimer,"engine.timer",80);

Is this even possible or do I need to connect it via
Code: [Select]
external to a PHP script?

marian

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #1 on: March 22, 2018, 01:42:55 AM »
There is no need to fill the 'id' parameter: the wave module will return its channel id in it along with 'peerid'.

You'll have to track the channel (watch for chan.disconnected) to detect termination and do what you need to do.

asymetrixs

  • Administrator
  • Newbie
  • *****
  • Posts: 47
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #2 on: March 22, 2018, 12:41:18 PM »
Can you please provide a small javascript example, that kicks in after my call.execute.
My call.execute calls my SIP phone and plays the wave. Then after that the call is terminated.
So, what JS code can handle the chan.disconnected message and connect it to a new voice file? I tried for several hours and I cannot make it....documentation is very limited.

marian

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #3 on: March 23, 2018, 02:11:41 AM »
function onChanDisc(msg)
{
   Do something with msg.id
   For re-execute you may send a chan.masquerade
}

Message.install(onChanDisc,"chan.disconnected",100);

asymetrixs

  • Administrator
  • Newbie
  • *****
  • Posts: 47
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #4 on: March 28, 2018, 11:24:15 AM »
ok, found the answer here https://forum.yate.ro/index.php?topic=382.0

However, http://docs.yate.ro/wiki/How_to_do_routing_using_javascript says, that when a call is incoming, a new script instance is launched and the incoming call is connected to this instance (that is dedicated to the incoming call leg)

What if I create an outgoing call like this:

Code: [Select]
var executeMsg = new Message("call.execute", msg);
executeMsg.callto = "dumb/";
executeMsg.target = "123456";
executeMsg.caller = "test";
executeMsg.callername = "test";
executeMsg.dispatch();

I can then register handler, e.g.
Code: [Select]
Message.install(onChanDtmf,"chan.dtmf",80, "id", executeMsg.peerid);but do I need the 3rd and 4th parameter or can I go with
Code: [Select]
Message.install(onChanDtmf,"chan.dtmf",80);, too?

What is the difference, is the first method a way to route the messages to the correct script instance whereas the 2nd (without the last parameters) routes messages to any instance?


« Last Edit: March 28, 2018, 11:53:09 AM by asymetrixs »

marian

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #5 on: March 29, 2018, 01:08:32 AM »

The first usage installs the message handler with a filter for parameter 'id'.
Messages are not routed: if you are installing a filter the internal dispatcher won't call your handler if its filter is not matching.

asymetrixs

  • Administrator
  • Newbie
  • *****
  • Posts: 47
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #6 on: March 30, 2018, 06:11:13 AM »
ok, so I have a JS script now that listens for engine.timer and creates a call everytime engine.timer fires (basically every second)

Code: [Select]
           
function onTimer(msg)
{
    // Get DB Object
    var m = new Message("database");
    // Specify connection to use
    m.account = "ivrcon";
    // Define Query
    m.query = "SELECT OK, number, call FROM get_next_call();";

    // Run the Query
    if (m.dispatch())
    {
        if (m.rows > 0)
        {
            Engine.debug(Engine.DebugInfo, "CREATING CALL.EXECUTE");
           
            var executeMsg = new Message("call.execute", msg);
            executeMsg.callto = "dumb/";
            executeMsg.target = "123456";
            executeMsg.caller = "test";
            executeMsg.callername = "test";

            Engine.debug(Engine.DebugInfo, "DISPATCHING CALL.EXECUTE");

            if(executeMsg.dispatch())
            {
                Engine.debug(Engine.DebugInfo, "PEER ID: " + executeMsg.peerid);

                Message.install(onCallAnswered,"call.answered",80, "id", executeMsg.peerid);
                Message.install(onChanNotify,"chan.notify",80, "id", executeMsg.id);
                Message.install(onChanDtmf,"chan.dtmf",80, "id", executeMsg.peerid);
               
                // one of both parties can hangup
                Message.install(onChanDisconnected,"chan.disconnected",100, "id", executeMsg.peerid);
                Message.install(onChanDisconnected,"chan.disconnected",100, "id", executeMsg.id);
            }
        }
    }
    else
    {
        Engine.output("Query failed");
    }
   
    return true;
}


function onCallAnswered(msg)
{
    Engine.debug(Engine.DebugInfo, "Chan.Answered ID: " + msg.id);
    Engine.debug(Engine.DebugInfo, "Chan.Answered PeerID: " + msg.peerid);

    var chanAttachMsg = new Message("chan.masquerade");
    chanAttachMsg.message = "chan.attach";
    chanAttachMsg.id = msg.peerid;
    chanAttachMsg.source = "wave/play//usr/share/yate/sounds/prompt_for_destination.alaw";
    chanAttachMsg.notify = msg.id;
    chanAttachMsg.dispatch();

    wait_for_notify = true;
    msg.handled = true;

    return true;
}

Engine.debugName("ivr-handler");
Message.trackName("ivr-handler");
Engine.debugEnabled(true);

// Message handler
Message.install(onTimer,"engine.timer",80);

So, I wonder if I need to install the handler with the "id" parameter and the filter for the message IDs. Anyway, like this, many calls will be created from yate in parallel, so there will be lots of chan.dtmf, chan.notify, chan.disconnected etc. messages. This leads me to the following question:

1. When engine.timer fires and the handler will call "onTimer", is this a new script instance every time or will the same script be used?
2. Are the executed calls and their messages always received by the same instance (does each call have it's own instance (like question above)) and can I use "instance-only visible" variables to store states and additional information for processing? Or do I always need to track the "id" parameter in the handlers to know to which call a message belongs? Or is the same instance always receiving the message because of the installed filter on message.id?


Also, as I understand, as this whole script is not called in the normal routing process, functions like Channel.callJust/callTo etc. are not available.

Thank you.

marian

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #7 on: April 02, 2018, 01:20:18 AM »
A new script instance is always created for a script set in routing parameter in javascript.conf.
The Channel variable (along with its functions callJust ...) is available in such script (internally created) and describes the channel the script is attached to.

It is not a good idea to use a timer to generate calls in a such a script for the following reasons:
- Each instance will do it
- If there is no call active no new call will be generated

In a channel script you may use internal pre-defined functions onAnswered() or onDisconnected() to handle channel events. No need to install message handlers.
If you want to use a custom function name you must install a message handler for it.

You should make a decision on using a channel script or a global one.

You may

asymetrixs

  • Administrator
  • Newbie
  • *****
  • Posts: 47
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #8 on: April 03, 2018, 01:23:31 PM »
In my example I am not using javascript.conf routing= to register the routing script. I just have a line at the bottom after [scripts] like
Code: [Select]
myivr=ivr.js and this ivr.js script is listening for the engine.timer message and initiates a call (if the database returns specific data)
So calls are only initiated if the database "says so".

So, what I basically want is to trigger yate to initiate an outgoing call that is then internally connected to an IVR, preferably written in Javascript.

As far as I understood:
- each JS script is creating a new instance when called, not only routing=<script.js> will result in 20 instances for 20 routing messages but also the custom defined scripts at the bottom will be called in an instance each time they are fired.
- my engine.timer fires up new instances of the script every time it elapses
- within this script I can use the predefined functions like onAnswered, onDisconnected, so I don't need to register my own handlers

How can I specify if my script runs per channel or only once globally?

marian

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: Yate outgoing call connected to javascript IVR
« Reply #9 on: April 04, 2018, 01:10:50 AM »
Multiple instances are built for 'routing' script only (if configured). Global scripts exist in a single instance only.

A routing script instance is attached to a call leg when a call.preroute/call.route is handled for a channel.
Functions handled: onStartup, onPreroute (if applicable), onExecute, onRinging, onAnswered, onDisconnected. These functions will be called, if implemented, on corresponding channel events. No need to install a message handler for them.
Route event is handled in script main flow.
At all time current call leg id is accessible with Channel.id
For extra events (like DTMFs) you may install a message handler for them.
If you are making outbound calls internally generated you should install a filter matching the peer id.

You may always use a global script.
Keep a list of active calls (track chan.disconnected to remove a call leg from list).
For channel events (ringing, answer ...) you must define your functions and install a handler for them.