This site is no longer updated.Go to new Conversational Cloud docs

Bot script for a voice broadcast


Text-to-speech and speech-to-text functions are used in voice broadcasts.

Text-to-speech — the text is converted to voice.

Speech-to-text — the sound stream is converted into text.

That aspect of bot script development has certain peculiarities. This article considers the following aspects:


Adapting the script for a telephone channel

Recognition

When adapting the bot script for a phone channel, you need to catch and handle the event speechNotRecognized. The event is triggered if a client request could not be recognized within 5 seconds, or if the recognition returned nothing.

For example:

  • The bot catches the event and requests the client to repeat the phrase once more:
    state: speechNotRecognized || noContext = true
        event: speechNotRecognized
        a: Please repeat, I can’t hear you.
        go!: {{$session.lastState}}
  • One of the following phrases is selected:
    state: CatchAll
        event!: speechNotRecognized
        q!: *
        random:
            a: Could you repeat please?
            a: I didn’t catch that.
            a: Please repeat.
            a: I’m sorry, I couldn’t hear what you said.
  • Let us consider an example when the bot encounters a voice message system.

Please note that pre-paid minutes in your subscriber plan will be spent while the bot is talking to an automated voice message system.

The bot asks the client to repeat the reply or hangs up if it could not recognize the phrase:

    state: NoInput || noContext=true
        event: speechNotRecognized
        script:
            $session.noInputCounter = $session.noInputCounter || 0;
            $session.noInputCounter++;
        
        if: $session.noInputCounter >= 5
            a: It seems that the line is bad. I will call back later.
            script:
                $dialer.hangUp('(The bot has hung up)');
        else:
            a: I cannot hear you, please repeat!

Other event for the telephone channel


Call termination by the bot

The bot cannot use the method $dialer.hangUp('(Bot has hung up)') to terminate a call by itself.

The phrase Bot has hung up will be displayed in the non-voice channel that you are using for bot debugging.

For example:

    state: HangUp
        a: Have a good day. Goodbye.
        script:
            $dialer.hangUp('(The bot has hung up)');

The argument for $dialer.hangUp() is optional.

Use event: botHangup to track an event when the conversation has been terminated at the request of the bot.

For example:

  state: BotHangUp
    event: botHangup
    script:
        log("Got event botHangup");

Call termination by the client

Use event: hangup to track the event of call termination by the client.

For example, to determine what state the client was in before the call was terminated:

    state: ClientHungUp
        event!: hangup
        script:
            getClientHangUpReaction($session.lastState);

Here, if the bot terminates the call by itself, the hangup event is not triggered.


Transfer to the operator

You can also transfer the call to an operator:

script:
    $response.replies = $response.replies || [];
    $response.replies
     .push({
        type:"switch",
        phoneNumber:88008000000,
    });

More about call transfers to operators


Tone dialing

The bot may request and process the sending of dtmf messages (numbers/characters coded as tones).

A dtmf message request:

script:
    $response.replies = $response.replies || [];
    $response.replies.push({
        type: 'dtmf',
        max: 4,            //the maximum number of digits expected from the subscriber.
        timeout: 15000     //the subscriber input timeout, in milliseconds (a number).
     });

Then a request is caught, for example, with the digits:

state: Digits
    q: $regexp<\d+>
    a: you have dialed {{$parseTree.text}}!

The event noDtmfAnswerEvent is triggered if the subscriber has not input a dtmf message:

    state: noDTMF || noContext = true
        event: noDtmfAnswerEvent
        a: You have not selected anything.
        go!: {{$session.lastState}}

More details about tone dialing


Sessions

More about sessions in the JAICP platform

In voice broadcasts a session starts when the subscriber picks up the receiver or makes a call themselves. Call termination is treated as the session termination.

Here, if you use the same script for other channels, then the session for them will start at the time of the first call by the client when there is no other active session for this client.

We do not recommend artificial session splitting for voice broadcasts using the newSession response.


Working with a phone number list

Managing telephone number lists

When creating a phone number list for a voice broadcast, you can add additional fields to the list. For example, First name, Last name, City. The table will only be validated by the first column that contains the phone numbers and the remaining fields will be loaded as text.

You can use the data from the additional fields in a broadcast script.

The method $dialer.getPayload() returns a json object with populated phone number list fields.

For example, we are uploading a phone number list:


phone name address
79990000000ᅠᅠ John ᅠᅠ ᅠᅠ New York

Calling the method $dialer.getPayload() will return a json object:

{
  "phone": "79990000000",
  "name": "John",
  "address": "New York"
}

Script use:

    state: Hello
        a: Hello!
        if: $dialer.getPayload().name
            a: Your name is {{$dialer.getPayload().name}}
        else:
            a: I don’t know your name.

If the list has been uploaded with headers, then calling the method $dialer.getPayload() will return a json object:

{0: "79990000000", 1: "John", 2: "New York"}

In this case var name = $dialer.getPayload()[1].

If the column is empty, then calling the $dialer.getPayload().address function will return an empty value.

{
  "phone": "79990000000",
  "name": "John",
  "address": ""
}

The method $dialer.getCaller() returns a string with the user’s phone number in the format in which the telephone switchboard sends it.

Script use:

    state: Hello
        a: Hello!
        a: Your number is {{getCaller()}}.
        if: $dialer.getPayload().name
            a: Your name is {{$dialer.getPayload().name}}
        else:
            a: I don’t know your name.

Scheduling a call from a script

You may need to schedule a new call during a conversation between a bot and a client. For example, a client may ask you to call later.

Use the $dialer.redial() method to schedule a new call and override the calling policy for this number from your script.

Parameters of this method match those of the CallJobParametersList method in Calls API.


Parameter Example Description
startDateTime 2020-03-23T00:00:00Z Call start date. Takes the Date object.

The call will be made
within the [startDateTime, finishDateTime] interval.
finishDateTime 2020-03-23T00:00:00Z Call end date. Takes the Date object.

Calls will not be made after finishDateTime.
allowedTime "mon": [{"localTimeFrom": "10:00", "localTimeTo": "11:30"}] For each day of the week, one or several recommended calling intervals can be specified.

localTimeFrom is the earliest time for a call. The time value is not absolute: customer’s local time will be used for calling.

localTimeTo is the latest time for a call. The time value is not absolute: customer’s local time will be used for calling.
allowedDays ["sun"] Weekdays when calling is allowed.
maxAttempts 1 Callback attempts.
retryIntervalInMinutes 120 The pause (in minutes) between callback attempts.
gmtZone "+03:00" Timezone ID. Should be specified from "-18:00" to "+18:00" or as the name of zone according to IANA TZBD.

Please note that $dialer.redial() method is not used without specified parameters. To ensure the correct work of the method, it is necessary to set the parameter startDateTime or allowedTime. If the rest of the parameters are not specified, their values will be taken from the voice calling campaign settings.

For example, you configure the following callback parameters:

state:
    q!: Сall back
    script:
        $dialer.redial({
            allowedTime: {
                "mon": [{"localTimeFrom": "10:00", "localTimeTo": "11:30"},
                        {"localTimeFrom": "13:00", "localTimeTo": "14:30"}],
                "fri": [{"localTimeFrom": "12:30", "localTimeTo": "15:00"}],
                "default": [{"localTimeFrom": "10:00", "localTimeTo": "18:00"}]
            },
            retryIntervalInMinutes: 120,
            maxAttempts: 3
         })
    a: ok

For the recommended interval, both the localTimeFrom and localTimeTo bounds must be specified. If one of the boundaries is not specified, then an error is returned.

In this example, the callback will be made on Monday from 10:00 to 11:30 and from 13:00 to 14:30, and on Friday from 12:30 to 15:00. Recommendations for others not explicitly specified days are specified in the allowedTime parameter with the default key.


If the allowedDays parameter is specified, the call can be made only on the day that is explicitly specified.

state:
    q!: Сall back
    script:
        $dialer.redial({
            allowedDays: ["sat", "sun"],
            allowedTime: {
                "fri": [{"localTimeFrom": "12:30", "localTimeTo": "15:00"}],
                "default": [{"localTimeFrom": "10:00", "localTimeTo": "18:00"}]
            },
             retryIntervalInMinutes: 120,
            maxAttempts: 3
         })
    a: ok

In this example, although the interval is explicitly specified for Friday in the allowedTime parameter, the call can only be made on Saturday or Sunday.

Please note that the recommended intervals within one day must be specified without intersections.

Examples of intersections:

"mon": [{"localTimeFrom": "10:00", "localTimeTo": "13:30"}, 
        {"localTimeFrom": "13:00", "localTimeTo": "14:30"}],
"tue": [{"localTimeFrom": "10:00", "localTimeTo": "18:30"}, 
         {"localTimeFrom": "13:00", "localTimeTo": "14:30"}],

If the localTimeFrom parameter is greater than localTimeTo in the specified interval, then the interval is considered to be correct. It starts on the specified day and ends the next day. The calls in the following example will be made from 8 p.m. Monday to 3 a.m. Tuesday:

"mon": [{"localTimeFrom": "20:00", "localTimeTo": "03:00"}],

Configure callback parameters based on the client’s reply. Use the $parseTree._Date.timestamp filled slot to define the call start date:

state: Call me back
        intent!: /Call me back
        a: Ok, I will call you back!
        script:
            $dialer.redial({startDateTime: new Date($parseTree._Date.timestamp)})
            $dialer.hangUp();

Configure callback settings at the client’s request with additional parameters:

state: 
    q!: call me back in a hour
	script:
	   var redialTimeFrom = moment().add(60, "m").toDate();
       var redialTimeTo = moment().add(75, "m").toDate();
	   $dialer.redial({
          startDateTime: redialTimeFrom, finishDateTime: redialTimeTo,   // call back in the interval of [now + 1h, now + 1h 15m]
          localTimeFrom: "12:00 AM", localTimeTo: "12:00 AM",                  // ignore call time limits based on the customer’s time
          maxAttempts: 2,                                                // make 2 calling attempts
          retryIntervalInMinutes: 5                                      // with a 5 minute pause between them
       })
	a: Ok, I will call you back in an hour

You can only schedule one call in a conversation. For example, a client may ask the bot to call later. Calls to $dialer.redial() are ignored for subsequent client replies as callback parameters have already been set.

Please note that $dialer.redial() parameters can be overridden if the bot did not reply. It can be done in postProcess or another state if a go! transition was made without a bot reply.

You can schedule up to 5 callbacks per number for a single voice calling campaign from your script.

Learn more about call priority and calling policy

</br

Tagging the broadcast outcomes

By tagging broadcast outcomes in a script you can add user-defined metrics into the statistics.

More about statistics

The method $dialer.setCallResult allows tagging of the broadcasting outcomes for keeping extended statistics.

For example, let us consider the following script:

        state: Yes
            q: Yes
            a: Thank you for your cooperation! Goodbye!
            script:
                $dialer.setCallResult("YES");
                $dialer.hangUp();
            
        state: No
            q: No
            a: I’m sorry to hear that you’re not interested. Goodbye!
            script:
               $dialer.setCallResult("NO");
               $dialer.hangUp();
            
        state: Maybe
            q: Maybe
            a: We will call you back later. Talk to you soon!
            script:
                $dialer.setCallResult("MAYBE");
                $dialer.hangUp();

Now the broadcast outcome will be displayed in the table with the metrics when the script reaches the specific state: YES, NO, MAYBE.

If extended metrics are not needed for the script, use methods that tag the outcomes about accepting or declining a proposal while not requiring the name to be filled in.

The methods $dialer.setCallResultAccepted and $dialer.setCallResultRejected make it possible to tag the outcome as ACCEPTED or REJECTED, respectively.

Script:

        state: Yes
            q: Yes
            a: Thank you for your cooperation! Goodbye!
            script:
                $dialer.setCallResultAccepted();
                $dialer.hangUp();
            
        state: No
            q: No
            a: I’m sorry to hear that you’re not interested. Goodbye!
            script:
               $dialer.setCallResultRejected();
               $dialer.hangUp();

In the table with the metrics the outcomes of the broadcast will be displayed as ACCEPTED and REJECTED when specific states are reached.


Extending the broadcast report

The method $dialer.reportData($header, $value, $order) makes it possible to add columns with arbitrary data to a broadcast .xls report.

Method parameters:

  • $header (string) — the column name in the report;
  • $value (string) — the value;
  • $order (number) — the sorting order of the columns in the exported .xls report.

The script developer may add columns into the report that are needed for a specific broadcast, for example:

  1. Retrieving data during a call:
    • $dialer.reportData("Full Name", "John Doe") — a Full Name column will be added to the broadcast outcome report, and the value John Doe will be inserted.
  2. To log the phrase and the completion of a step:
    • $dialer.reportData("Step_Refused", "I don’t need it") — a Step_Refused column will be added to the broadcast outcome report and it will contain the phrase I don’t need it.

The $order parameter is optional and takes the default value of 0 if omitted. The columns in the report are arranged in descending order depending on the corresponding $order values, and if it is the same for multiple columns, these will be sorted alphabetically.

Consider the following script:

theme: /

    state:
        q!: *start
        a: Hello! What is your name?

        state: Name
            q: *
            a: Hello, {{$parseTree.text}}!
            script:
                $dialer.reportData("Name", $parseTree.text);
            a: What do you think about smoking?

            state: Smoking
                q: *
                a: Okay, we’ll put it down as: "{{$parseTree.text}}".
                script:
                    $dialer.reportData("Attitude to smoking", $parseTree.text, 1);

Here the Attitude to smoking column will be the first of the user-defined columns as its $order is 1. The Name column will be placed second, because its $order is omitted and therefore equals 0.

In a more complex case, the columns will be sorted this way in accordance with their $order values:

Sex First Name Last Name Patronymic Attitude to smoking
1 0 0 0 -1

Please note that the number of columns in the broadcast report is limited. Ask your account administrator for details.

Retrieving call recordings

If the phone channel has its Record calls parameter set to active, you can retrieve links to ongoing calls directly from the script using the $dialer.getCallRecordingPath() method.

The method returns the relative path to the current call recording, such as the following:

242829491/2019-06-18/79313365671/16:56:31.55-1P

The full path conforms to this template:

https://{{host}}/restapi/download/{{projectId}}/recordings/call/{{callRecordingPath}}

Here,

  • host is the domain where the project is hosted, e.g. app.jaicp.com.
  • projectId is an integer project identifier, e.g. 242829491.
  • callRecordingPath is the value returned by $dialer.getCallRecordingPath.