Guest

Cisco TCL Scripts for IOS Gateways

TCL IVR API Version 1.0 Programmer's Guide

Table Of Contents

TCL IVR API Version 1.0 Programmer's Guide

Overview of IVR and TCL

Plans for Developing a New Script Format

Prerequisites

Restrictions

Developer Support

How to Use TCL IVR Scripts

Writing an IVR Script using TCL Extensions

Testing and Debugging Your Script

Loading Your Script

Associating Your Script with an Inbound Dial Peer

Displaying Information About IVR Scripts

Using URLs in IVR Scripts

Tips for Using Your TCL IVR Script

TCL IVR Command Reference

acceptCall

ani

authenticate

authorize

callID

callProceeding

clearOutgoingLeg

conferenceCreate

conferenceDelete

creditTimeLeft

did

dnis

exit

failureCode

getVariable

insertMessage

insertTone

placeCall

playFailureTone

playPrompt

playTone

promptAndCollect

puts

rdn

setLocation

setTimeout

setVariable

setupAck

startTimer

waitEvent

Value Definitions

Events

ReturnInfo Array

Tones

Related Documentation

Glossary

Cisco Connection Online

Documentation CD-ROM


TCL IVR API Version 1.0 Programmer's Guide


Version Date: 03/11/02

This document contains information about the Tool Command Language (TCL) Interactive Voice Response (IVR) application programming interface (API) Version 1.0 commands that you can use to write TCL scripts to interact with the Cisco IVR feature. It provides an annotated example of a TCL IVR script and includes instructions for testing and loading a TCL IVR script.

This document helps developers writing voice application software for Cisco voice interfaces, such as the Cisco AS5x00 series. This includes independent software vendors (ISVs), in-house corporate developers, system integrators, and Original Equipment Manufacturers (OEMs).

This document includes the following sections:

Overview of IVR and TCL

How to Use TCL IVR Scripts

TCL IVR Command Reference

Related Documentation

Glossary

Cisco Connection Online

Documentation CD-ROM

Overview of IVR and TCL

IVR is a term that is used to describe systems that provide information in the form of recorded messages over telephone lines in response to user input in the form of spoken words, or more commonly dual tone multifrequency (DTMF) signaling. For example, when a user makes a call with a debit card, an IVR application is used to prompt the caller to enter a specific type of information, such as a PIN. After playing the voice prompt, the IVR application collects the predetermined number of touch tones (digit collection), forwards the collected digits to a server for storage and retrieval, and then places the call to the destination phone or system. Call records can be kept and a variety of accounting functions performed.

The IVR application (or script) is a voice application designed to handle calls on a voice gateway, which is a router that is equipped with Voice over IP (VoIP) features and capabilities.

The prompts used in an IVR script can be either static or dynamic. With static prompts, the prompt message is pre-recorded in its entirety and played as a single unit. With dynamic prompts, the prompt message is pre-recorded in pieces and the pieces are assembled dynamically during play. For example, dynamic prompts are used to inform the caller of how much time is left in their debit account, such as:

"You have 15 minutes and 32 seconds of call time left in your account."

The above prompt is created using eight individual prompt files. They are: youhave.au, 15.au, minutes.au, and.au, 30.au, 2.au, seconds.au, and leftinyouraccount.au. These audio files are assembled dynamically by the underlying system and played as a prompt based on the selected language and prompt file locations.

The Cisco IVR (Interactive Voice Response) feature, available in Cisco IOS Release 12.0(6)T and later, provides IVR capabilities using TCL 1.0 scripts. These scripts are signature locked, and can be only modified by Cisco. The IVR feature allows IVR scripts to be used during call processing. The scripts interact with the IOS software to perform various call-related functions. Starting with release 12.1(3), the TCL script lock will be removed, thus allowing customers to create and change their own TCL scripts.

TCL is an interpreted scripting language. Because TCL is an interpreted language, scripts written in TCL do not have to be compiled before they are executed. TCL provides a fundamental command set, which allows for standard functions such as flow control (if, then, else) and variable management. By design, this command set can be expanded by adding "extensions" to the language to perform specific operations.

Cisco has created a set of extensions, called TCL IVR commands, that allows users to create IVR scripts using TCL. Unlike other TCL scripts, which are invoked from a shell, TCL IVR scripts are invoked when a call comes into the gateway.

The remainder of this document assumes that you are familiar with TCL and how to create scripts using TCL.If you are not, we recommend that you read TCL and the TK Toolkit by John Ousterhout (published by Addison Wesley Longman, Inc).

Plans for Developing a New Script Format

Cisco plans to develop a new TCL script format (TCL 2.0) that will address scalability and performance issues.

All verbs are non blocking, meaning that they can execute without causing the script to wait, which allows the script to perform multiple tasks at once.

The scripts are event-driven and the flow of the call is controlled by a Finite State Machine (FSM), which is defined by the TCL script.

Prompt can be played over VoIP and VoFR call legs.

Digits can be collected over VoIP and VoFR call legs.

Real-Time Streaming Protocol (RTSP)-based prompts are supported (depending on the release of Cisco IOS software and the platform).

Scripts can control more than two legs simultaneously.

Call legs can be handed off between scripts.

Prerequisites

The Open TCL IVR feature is currently supported on the Cisco AS5300 voice platform only. In order to use the open TCL IVR feature you need the following:

1. Cisco AS5300 (Voice platform) universal access server

2. Cisco IOS Release 12.1(3) or later.

3. TCL version 7.1 or later.

Calls can come into a gateway using analog lines, ISDN lines, a VoIP link, or a VoFR link. TCL IVR scripts can provide full functionality for calls received over analog or ISDN lines. The functionality provided for calls received over VoIP or Voice over Frame Relay (VoFR) links varies depending on the release of Cisco IOS software being used. For example, if you are using Cisco IOS Release 12.0, you cannot play prompts or tones, and you cannot collect tones.

Restrictions

The argument av-send, used with the authenticate and authorize commands, is valid on Cisco IOS Release 12.1(2) and later. Refer to the authenticate and authorize for more information.

If a TCL 1.0 script is used with the TCL Interpreter 8.0.5, the minimum memory requirement is 64 mbytes for the Cisco 2600 and Cisco 36xx platforms, starting with Cisco IOS Release 12.1(3)T. Additional memory is only needed if one uses TCL 1.0 scripts with TCL interpreter 8.0.5. At this time, the unlocked TCL scripts are not supported on the 2600 and the 36xx platforms. This memory requirement applies regardless of whether the TCL scripts are locked or unlocked.

Developer Support

Developers using this guide may be interested in joining the Cisco Developer Support Program. This new program has been developed to provide you a consistent level of support that you can depend on while leveraging Cisco interfaces in your development projects.

A signed Developer Support Agreement is required to participate in this program. For more details, and access to this agreement, please visit us at: http://www.cisco.com/warp/public/779/servpro/programs/ecosystem/devsup, or contact developer-support@cisco.com

How to Use TCL IVR Scripts

This section of the document includes information on how to create and use TCL IVR scripts. This section includes the following topics:

Writing an IVR Script using TCL Extensions

Testing and Debugging Your Script

Loading Your Script

Associating Your Script with an Inbound Dial Peer

Displaying Information About IVR Scripts

Using URLs in IVR Scripts

Tips for Using Your TCL IVR Script

Writing an IVR Script using TCL Extensions

Before you write an IVR script using TCL, you should familiarize yourself with the TCL extensions for IVR scripts.

You can use any text editor to create your TCL IVR script. Follow the standard conventions for creating a TCL script and incorporate the TCL IVR commands as necessary.

A sample script is provided in this section to illustrate how the TCL IVR commands can be used.


Note   When writing scripts, it's strongly recommended first doing a setupAck or an acceptCall.



Note   If the caller hangs up, the script stops running and the call legs are cleared. No further processing will be done by the script, which means the exit command will not be called.


Standard TCL Commands Used in TCL IVR Scripts

Certain standard TCL commands can be used in TCL IVR scripts. These commands are as follows:

append

array

break

case

catch

concat

continue

error

eval

expr

for

foreach

format

global

history

if

incr

info

join

lappend

lindex

linsert

list

llength

lrange

lreplace

lsearch

lsort

proc

puts

regexp

regsub

rename

return

set

split

string

switch

tcl_trace

unset

uplevel

upvar

while

 

For additional information about the standard TCL commands, see the TCL and the TK Toolkit by John Ousterhout (published by Addison Wesley Longman, Inc).

TCL IVR Commands At-a-Glance

In addition to the standard TCL commands, you can use the TCL extensions for IVR scripts that Cisco has created. Also, Cisco modified two of the existing TCL commands, puts and exit, to perform specific tasks. The TCL IVR commands are as follows:

Command
Description

acceptCall

Sends setup acknowledgement, call proceeding, and call connect messages to the incoming call leg.

ani

Returns the originating number, which is supplied by the incoming call leg.

authenticate

Sends an authentication request to an external system, typically a Remote Access Dial- In User Services (RADIUS) server.

authorize

Sends an authorization request to an external system, typically a RADIUS server.

callID

Returns the call ID used in the Cisco IOS debug messages.

callProceeding

Sends a call proceeding message to the incoming call leg.

clearOutgoingLeg

Deletes a conference and disconnects the outgoing call leg.

conferenceCreate

Creates a conference between two call legs.

conferenceDelete

Deletes a conference between two call legs.

creditTimeLeft

Returns the credit time available for this call.

did

Determines whether the incoming dial peer is configured for dial-in direct (DID) operation.

dnis

Returns the destination number.

exit

Exits the script, clears the call leg, and cleans up the data structures.

failureCode

Returns the last failure event and code.

getVariable

Returns the value of a specified variable.

insertMessage

Inserts a message to the originating (calling) party.

insertTone

Inserts a beep to the originating (calling) party.

placeCall

Places a call to the specified destination.

playFailureTone

Plays one of two tones depending on the last failure event.

playPrompt

Plays a prompt to the incoming call leg. Designed for use with dynamic prompts.

playTone

Plays a call progress tone.

promptAndCollect

Plays a prompt to the originating (calling) party and collects digits in a specified pattern.

puts

Outputs a debug string to the console if the IVR state debug flag is set.

rdn

Returns the redirect number that came with the call.

setLocation

Sets the location of the audio files.

setTimeout

Sets the initial digit collection timeout and the inter-digit collection timeout values.

setVariable

Sets the value of a specified variable.

setupAck

Sends a call setup acknowledgement back to the incoming call leg.

startTimer

Causes the script to block until a specified number of seconds passes or an event (such as a destination hang up or an origination hang up) occurs.

waitEvent

Causes the script to pause until an event occurs.


For more information about these commands, see the "TCL IVR Command Reference" section.

Sample TCL IVR Script

The following annotated example illustrates how the TCL IVR commands can be used.

We start with the header information, which includes the name of the script, the date the script was created and by whom, and some general information about what the script does.

It is also a good idea to include a version number for the script. We recommend that you use a three digit system where the first digit indicates a major version of the script, the second digit should be incremented with each minor revisions (such as a change in function within the script), and the third number should be incremented each time other changes are made to the script.

# Script Loaded by: Dram
# Script Version 1.0.1
# Script LookDate: Sept. 30 20:58:49 1999
#------------------------------------------------------------------
# September 1998, David Ramsthaler
#
# Copyright (c) 1998, 1999, 2000 by cisco Systems, Inc.
# All rights reserved.
#------------------------------------------------------------------
#
# This script authenticates using (ani, dnis) for (account, password). If
# that fails, it collects account and pin number, then authenticates
# using (account, pin). 
#
# If authentication passes, it collects the destination number and
# places the call.
# 

Next, we define a series of procedures.

The get_account procedure defines the parameters of the account prompt and collects the account information. In this procedure:

The prompt plays an audio file called enter_account, which is stored in Flash memory.

The user is allowed to enter information before the prompt message is complete.

The user is allowed to abort the process by pressing the asterisk key.

The user must indicate that they have completed their entry by pressing the pound (#) key.

The input from the user is collected in an array.

If the collection of information is successful, the script stores the account number and indicates that the next procedure is to get the personal identification number (PIN).

If the user aborts the process, the script indicates that this procedure should be started again.

If the account number is not collected, the script indicates that this procedure should be started again.

proc do_get_account {} {
    global state
    global account

    set prompt(url) flash:enter_account.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set patterns(account) .+
    set event [promptAndCollect prompt info patterns ]

    if {$event == "collect success"} {
        	set state get_pin
        	set account $info(digits)
        	return 0
    }

    if {$event == "collect aborted"} {
        	set state get_account
        	return 0
    }

    if {$event == "collect fail"} {
        	set state get_account
        	return 0
    }
    set state end
    return 0
}

The get_pin procedure defines the parameters of the PIN prompt and collects the PIN. In this procedure:

The prompt plays an audio file called enter_pin, which is stored in Flash memory.

The user is allowed to enter information before the prompt message is complete.

The user is allowed to abort the process by pressing the asterisk (*) key.

The user must indicate that they have completed their entry by pressing the pound (#) key.

The input from the user is collected in an array.

If the collection of information is successful, the script stores the PIN and indicates that the next procedure is to authenticate the information.

If the user aborts the process, the script indicates that the user should be returned to the account collection procedure.

If the user does not enter a PIN within the allotted time, the script indicates that this procedure should be started again.

If the PIN entered by the user is invalid, the script indicates that this procedure should be started again.

proc do_get_pin {} {
    global state
    global pin

    set prompt(url) flash:enter_pin.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set patterns(account) .+
    set event [promptAndCollect prompt ReturnInfo patterns ]

    if {$event == "collect success"} {
        	set state authenticate
        	set pin $info(digits)
        	return 0
    }

    if {$event == "collect aborted"} {
        	set state get_account
        	return 0
    }

    if {$event == "collect fail"} {
        	# timeout
        	if {$info(code) == 102} {
        	    set state get_pin
        	    return 0
	}

        	# invalid number
        	if {$info(code) == 28} {
        	    set state get_pin
        	    return 0
	}
    }

    set state end
    return 0
}

The authenticate procedure passes the information collected to a RADIUS server for authentication and waits for a reply. In this procedure:

The account number and PIN are passed to the RADIUS server.

The response from the RADIUS server is interpreted.

If the authentication is successful, the script indicates that the next state is to continue processing the call.

If the authentication fails, the script indicates that the next state is to inform the user of the failure.

proc do_authenticate {} {
    global state
    global pin
    global account

    set event [authenticate $account $pin info]

    if { $event == "authenticated" } {
         	set state authen_pass
         	return 0
    }

    if {$event == "authentication failed"} {
        	set state authen_fail
        	return 0
    }

    set state end
    return 0
}

The get_dest procedure defines the parameters of the destination prompt and collects the destination phone number. In this procedure:

The prompt plays an audio file called enter_destination, which is stored in Flash memory.

The user is allowed to enter information before the prompt message is complete.

The user is allowed to abort the process by pressing the asterisk (*) key.

The user must indicate that they have completed their entry by pressing the pound (#) key.

The destination phone number is collected against the dial plan.

If the collection of information is successful, the script indicates that the next procedure is to process the call.

If the user aborts the process, the script indicates that this procedure should be started again.

If the destination phone number entered by the user is invalid, the script indicates that this procedure should be started again.

proc do_get_dest {} {
    global state
    global destination

    set prompt(url) flash:enter_destination.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set prompt(dialPlan) true

    set event [promptAndCollect prompt info ]

    if {$event == "collect success"} {
        	set state place_call
        	set destination $info(digits)
        	return 0
    }

    if {$event == "collect aborted"} {
        	set state get_dest
        	return 0
    }

    if {$event == "collect fail"} {
        	set state get_dest
        	return 0
    }
    set state end
    return 0
}

The authen_pass procedure determines whether the destination phone number has been provided. In this procedure:

If the incoming dial peer is configured for DID operation and there is an incoming called number ($dnislen is not equal to 0), the destination is set to the destination number and the script indicates that the next state is place_call.

If the incoming dial peer in not configured for DID operation, the script indicates that the next state is to collect the destination phone number.

proc do_authen_pass {} {
    global state
    global destination

    set dnislen [string len [dnis]]

    if { [did] && $dnislen } {
        	set destination [dnis]
        	set state place_call
    } else {
        	set state get_dest
    }
    return 0
}

The place_call procedure places the call to the specified destination. In this procedure:

If the call is successful, the script indicates that the next state is to determine whether the call should be timed.

If the call is unsuccessful, the script indicates that the next state is to process the failed call.

proc do_place_call {} {
    global state
    global destination
    
    set event [placeCall $destination callInfo info]

    if {$event == "active"} {
        	set state active
        	return 0
    }
    if {$event == "call fail"} {
        	set state place_fail
        	return 0
    }

    set state end
    return 0
}

The active_notimer procedure specifies that no time limit is set on the call. The script waits idly for an event.

proc do_active_notimer {} {
    global state

    set event [waitEvent]
    while { $event == "digit" } {
        	set event [waitEvent]
    }
    set state end
    return 0
}

The active_last_timer procedure monitors the last few seconds of time allowed for the call to ensure that the user does not exceed their allotted credit time. In this procedure:

A final timer is set. The duration of the timer is equal to the number of seconds left in the user's account.

If the call exceeds the timer, the script disconnects the outgoing call leg and indicates that the next procedure is to notify the user that they are out of time.

proc do_active_last_timer {} {
    global state

    set event [startTimer [creditTimeLeft] info]
    while { $event == "digit" } {
        	set event [startTimer $info(timeLeft) info]
    }
    if { $event == "timeout" } {
        	clearOutgoingLeg returnInfo
        	set state out_of_time
    } else {
        	set state end
    }

    return 0
}

The active_timer procedure monitors the amount of time used during the call to ensure that the user does not go past their allotted credit time. In this procedure:

The user's remaining amount of credit is determined.

If the user has less than ten seconds remaining, the script indicates that the next procedure should be to monitor the last few seconds of the call.

If the user has more than ten seconds remaining, a timer is set that equals the user's remaining time minus ten seconds.

If the call exceeds the timer, a warning message (beep.au) is played and the script indicates that the next procedure is to monitor the last few seconds of the call.

proc do_active_timer {} {
    global state

    if { [creditTimeLeft] < 10 } {
        	do_active_last_timer
        	return 0
    }
    set delay [expr [creditTimeLeft] - 10]
    set event [startTimer $delay info]
    while { $event == "digit" } {
        	set event [startTimer $info(timeLeft) info]
    }
    if { $event == "timeout" } {
        	insertMessage flash:beep.au returnInfo
        	do_active_last_timer
    } else {
        	set state end
    }

    return 0
}

The active procedure determines whether there is a time limit on the call. In this procedure:

If the user has no credit limit, the script indicates that the next procedure is to wait idly for an event.

If the user has a credit limit, the script indicates that the next procedure is to start an active timer.

proc do_active {} {
    global state

    if { ( [creditTimeLeft] == "unlimited") || 
         ([creditTimeLeft] == "uninitialized") } {
	     do_active_notimer
    } else {
	     do_active_timer
    }
    return 0
}

The out_of_time procedure plays a message (out_of_time.au) if the user is out of time.

proc do_out_of_time {} {
    global state

    set prompt(url) flash:out_of_time.au
    set prompt(playComplete) true
    set event [promptAndCollect prompt info ]
    set state end
    return 0
}

The authen_fail procedure plays a message (auth_failed.au) if the information that the user provided is not authenticated by the RADIUS server.

proc do_authen_fail {} {
    global state

    set prompt(url) flash:auth_failed.au
    set prompt(playComplete) true
    set event [promptAndCollect prompt info ]
    set state end
    return 0
}

The place_fail procedure plays a tone if the call cannot be placed.

proc do_place_fail {} {
    global state

    playFailureTone 5 returnInfo
    set state end
    return 0
}

Finally, we put all the procedures together in a main routine. The main routine passes the origination and destination phone numbers (if available) to the RADIUS server for authentication. If the response from the RADIUS server is "authenticated," the authen_pass procedure is called.

If the response from the RADIUS server is anything other than "authenticated," the get_account procedure is called.

From there, the main routine reads the state as set by each procedure to determine which procedure to call next.

#-------------------------------------------------------
# The main routine begins here.
#

acceptCall

set event [authenticate [ani] [dnis] info]

if {$event != "authenticated"} {
    set state get_account
} else {
    set state authen_pass
}

while {$state != "end"} {
      if {[info proc do_$state] == do_$state}
      do_$state
}

Testing and Debugging Your Script

It's important to thoroughly test a script before it is deployed.

To test a script, you must place it on a router and place a call to activate the script. When you test your script, make sure that you test every procedure in the script and all variations within each procedure. Errors that occur in parts of the script that are not tested will not be found. For example, if you have the following:

if {[dnis]=="1234"} {
   promptAndcollect prompt info

The misspelling of the promptAndCollect command will not be detected until the number 1234 is dialed.

You can view debugging information applicable to the TCL IVR scripts that are running on the router. The debug voip ivr command allows you to specify the type of debug output you want to view. To view debug output, enter the following command in privileged Exec mode:

[no] debug voip ivr [states | error | script | dynamic| all]

If you specify "error," output is displayed only if an error occurs in the script. If you specify "states," the output provides information about what is happening in an IVR application. If you specify "dynamic" this shows the dynamic prompt play information. If you specify "all," both error and states information is displayed.

The output of any TCL puts commands is displayed if script debugging is on.


Note   Possible sources of errors are: an unknown or misspelled command (for example, if you misspell promptAndCollect as promptAndcollect), a syntax error (such as, specifying an invalid number of arguments), or executing a command in an invalid state (for example, executing insertMessage when no conference is active). In most cases, an error such as these will cause the underlying infrastructure to disconnect the call and clean up.


Error Handling of TCL IVR Commands

Following are some of the errors that may appear and their causes:

Error Message
Description

wrong # args: should be "cmd... ..."

Script exits with error information due to the wrong number of arguments.

bad args: should be "cmd... ..."

Script exit with error information due to incorrect argument(s).

verb-name called in wrong state, ... other detail information...

Script exit with error information due to verb being called in the wrong state.

verb-name ignored, ... other detail information

Command is ignored due to verb being called in the wrong state.


Loading Your Script

To associate an application with your TCL IVR script and load the script, use the following command:

(config)#call application voice script_name url [parameter value]

In this command:

script_name specifies the name of the TCL application that the system is to use for the calls configured on the inbound dial peer. Enter the name to be associated with the TCL IVR script.

url is the pathname where the script is stored. Enter the pathname of the storage location first and then the script filename. TCL IVR scripts can be stored in Flash memory or on a server that is acceptable using a URL, such as a TFTP server.

parameter value allows you to configure values for specific parameters, such as language or PIN length. For more information about possible parameters, see the "Debit Card for Packet Telephony on Cisco Access Platforms" document on CCO.

In the following example, the application named "test" is associated with the TCL IVR script called newapp.tcl, which is located at tftp://keyer/debit_audio/.

(config)#call application voice test tftp://keyer/debit_audio/newapp.tcl

If you modify your script, you can reload it using only the script name as shown below:

(config)#call application voice load script_name

Associating Your Script with an Inbound Dial Peer

To invoke your TCL IVR script to handle a call, you must associate the script with an inbound dial peer. To associate your script with an inbound dial peer, enter the following commands in configuration mode:

(config)#dial-peer voice number voip

(conf-dial-peer)#incoming called-number destination_number

(conf-dial-peer)#application application_name

In these commands:

number uniquely identifies the dial peer. (This number has local significance only.)

destination_number specifies the destination telephone number. Valid entries are any series of digits that specify the E.164 telephone number.

application_name is the abbreviated name that you assigned when you loaded the application.

For example, the following commands indicate that the application called "newapp" should be invoked for calls that come in from an IP network and are destine for the telephone number of 125.

(config)#dial-peer voice 3 voip
(conf-dial-peer)#incoming called-number 125
(conf-dial-peer)#application test

For more information about inbound dial peers, see the Cisco IOS software documentation.

Displaying Information About IVR Scripts

To view a list of the voice applications that are configured on the router, use the show call application voice command. A one-line summary of each application is displayed. The description includes the names of the audio files the script plays, the operation of the interrupt keys, what prompts are used, and the caller interaction.

#show call application voice [ [name] | [summary] ]

In this command:

name indicates the name of the desired IVR application. If you enter the name of a specific application, the system supplies information about that application.

summary indicates that you want to view summary information. If you specify the summary keyword, a one-line summary is displayed about each application. If you omit this keyword, a detailed description of the specified application is displayed.

The following is an example of the output of the show call application voice command.

router#show call app voice clid_authen_collect
Idle call list has 0 calls on it.
Application clid_authen_collect
    The script is compiled into the image
    It has 0 calls active.
 
The TCL Script is:
------------------
# clid_authen_collect.tcl
#----------------------------------
# September 1998, David Ramsthaler
#
# Copyright (c) 1998, 1999 by cisco Systems, Inc.
# All rights reserved.
#----------------------------------
# Mimic the clid_authen_collect script in the SP1.0 release.
#
# It authenticates using (ani, dnis) for (account, password). If
# that fails, it collects account and pin number, then authenticates
# using (account, pin). 
#
# If authentication passes, it collects the destination number and
# places the call.
# 
# The main routine is at the bottom. Start reading the script there.
#
proc do_get_account {} {
    global state
    global account
 
    set prompt(url) flash:enter_account.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set patterns(account) .+
    set event [promptAndCollect prompt info patterns ]
 
    if {$event == "collect success"} {
        set state get_pin
        set account $info(digits)
        return 0
    }
 
    if {$event == "collect aborted"} {
        set state get_account
        return 0
    }
 
    if {$event == "collect fail"} {
        set state get_account
        return 0
    }
    set state end
    return 0
}
    
proc do_get_pin {} {
    global state
    global pin
 
    set prompt(url) flash:enter_pin.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set patterns(account) .+
    set event [promptAndCollect prompt info patterns ]
 
    if {$event == "collect success"} {
        set state authenticate
        set pin $info(digits)
        return 0
    }
 
    if {$event == "collect aborted"} {
        set state get_account
        return 0
    }
 
    if {$event == "collect fail"} {
        # timeout
        if {$info(code) == 102} {
            set state get_pin
            return 0
        }
 
        # invalid number
        if {$info(code) == 28} {
            set state get_pin
            return 0
        }
    }
 
    set state end
    return 0
}
 
proc do_authenticate {} {
    global state
    global pin
    global account
 
    set event [authenticate $account $pin info]
 
    if { $event == "authenticated" } {
        set state authen_pass
        return 0
    }
 
    if {$event == "authentication failed"} {
        set state authen_fail
        return 0
    }
 
    set state end
    return 0
}
 
proc do_get_dest {} {
    global state
    global destination
 
    set prompt(url) flash:enter_destination.au
    set prompt(interrupt) true
    set prompt(abortKey) *
    set prompt(terminationKey) #
    set prompt(dialPlan) true
 
    set event [promptAndCollect prompt info ]
 
    if {$event == "collect success"} {
        set state place_call
        set destination $info(digits)
        return 0
    }
 
    if {$event == "collect aborted"} {
        set state get_dest
        return 0
    }
 
    if {$event == "collect fail"} {
        set state get_dest
        return 0
    }
    set state end
    return 0
}
 
proc do_authen_pass {} {
    global state
    global destination
 
    set dnislen [string len [dnis]]
 
    if { [did] && $dnislen } {
        set destination [dnis]
        set state place_call
    } else {
        set state get_dest
    }
    return 0
}
 
proc do_place_call {} {
    global state
    global destination
    
    set event [placeCall $destination callInfo info]
 
    if {$event == "active"} {
        set state active
        return 0
    }
    if {$event == "call fail"} {
        set state place_fail
        return 0
    }
 
    set state end
    return 0
}
 
proc do_active_notimer {} {
    global state
 
    set event [waitEvent]
    while { $event == "digit" } {
        set event [waitEvent]
    }
    set state end
    return 0
}
 
proc do_active_last_timer {} {
    global state
          
    set event [startTimer [creditTimeLeft] info]
    while { $event == "digit" } {
        set event [startTimer $info(timeLeft) info]
    }
    if { $event == "timeout" } {
        clearOutgoingLeg retInfo
        set state out_of_time
    } else {
        set state end
    }
 
    return 0
}
 
proc do_active_timer {} {
    global state
 
    if { [creditTimeLeft] < 10 } {
        do_active_last_timer
        return 0
    }
    set delay [expr [creditTimeLeft] - 10]
    set event [startTimer $delay info]
    while { $event == "digit" } {
        set event [startTimer $info(timeLeft) info]
    }
    if { $event == "timeout" } {
        insertMessage flash:beep.au retInfo
        do_active_last_timer
    } else {
        set state end
    }
 
    return 0
}
 
proc do_active {} {
    global state
 
    if { ( [creditTimeLeft] == "unlimited") || 
         ([creditTimeLeft] == "uninitialized") } {
             do_active_notimer
    } else {
             do_active_timer
    }
    return 0
}
 
proc do_out_of_time {} {
    global state
 
    set prompt(url) flash:out_of_time.au
    set prompt(playComplete) true
    set event [promptAndCollect prompt info ]
    set state end
    return 0
}
 
proc do_authen_fail {} {
    global state
 
    set prompt(url) flash:auth_failed.au
    set prompt(playComplete) true
    set event [promptAndCollect prompt info ]
    set state end
    return 0
}
 
proc do_place_fail {} {
    global state
 
    playFailureTone 5 retInfo
    set state end
    return 0
}
 
#---------------------------------------
# And here is the main loop
#
 
acceptCall
 
set event [authenticate [ani] [dnis] info]
 
if {$event != "authenticated"} {
    set state get_account
} else {
    set state authen_pass
}
 
while {$state != "end"} {
    puts "cid([callID]) running state $state"
    if {$state == "get_account"} {
        do_get_account
    } elseif {$state == "get_pin"} {
        do_get_pin
    } elseif {$state == "authenticate"} {
        do_authenticate
    } elseif {$state == "get_dest"} {
        do_get_dest
    } elseif {$state == "place_call"} {
        do_place_call
    } elseif {$state == "active"} {
        do_active
    } elseif {$state == "authen_fail" } {
        do_authen_fail
    } elseif {$state == "authen_pass" } {
        do_authen_pass
    } elseif {$state == "place_fail"} {
        do_place_fail
    } elseif {$state == "out_of_time"} {
        do_out_of_time
    } else {
        break
    }
}

Using URLs in IVR Scripts

With IVR scripts, you use URLs to call the script and to call the audio files that the script plays. The VoIP system uses IOS File System (IFS) to read the files, so any IFS supported URLs can be used, which includes TFTP, FTP, or a pointer to a device on the router.


Note   Flash memory is limited to 32 entries, so it may not be possible to copy all your audio files into Flash memory.


URLs for Loading the IVR Script

The URL of the IVR script is a standard URL that points to the location of the script. Examples include:

flash:myScript.tcl—The script called myscript.tcl is being loaded from Flash memory on the router.

slot0:myscript.tcl—The script called myscript.tcl is being loaded from a device in slot 0 on the router.

tftp://BigServer/myScripts/betterMouseTrap.tcl—The script called myscript.tcl is being loaded from a server called BigServer in a directory within the tftpboot directory called myScripts.

URLs for Loading Audio Files

URLs for audio files are different from those used to load IVR scripts. With URLs for audio files:

For static prompts (used with the playPrompt, insertMessage, and promptAndCollect commands), you can use the IFS-supported URLs as described in the "URLs for Loading the IVR Script" section.

For dynamic prompts (used in the playPrompt command), the URL is created by the software using information from the setVariable language and setLocation commands and the language CLI configuration command.

Tips for Using Your TCL IVR Script

This section provides some answers to frequently asked questions about using TCL IVR scripts.

1 How do I get information from my RADIUS server to the TCL IVR script?

After you perform an authentication and authorization, you can use the getVariable command to obtain the credit amount, credit time, and cause codes maintained by the RADIUS server. See the "getVariable" section.

2 How do I keep the network from charging for the incoming call if there is no answer?

Although gateways do not control the charges for each call, in most networks you are only charged for completed calls. Therefore, if the incoming network does not detect a completed call, you are not charged. The placeCall command accepts an incoming call only if it succeeds in placing the call. At that point the incoming network will detect the completed call and charges will begin to accrue.

Normally, if the script is going to play prompts and collect digits, it will need to accept the incoming call by running the acceptCall command. If the script does not accept the incoming call, there may be a relatively short timer on the incoming network.

3 What happens if my script encounters an error?

When an error is encountered in the script, the call is cleared with a cause of TEMPORARY_FAILURE (41). If the IVR application has already accepted the incoming call, the caller will hear silence. If the script has not accepted the incoming call, the caller might hear a fast busy signal.

If the script exits with an error and IVR debugging is on (as described in "Testing and Debugging Your Script" section), the location of the error in the script is displayed at the command line.

TCL IVR Command Reference

This section provides an alphabetical listing of the new TCL IVR commands. The following is provided for each command:

Description of the purpose or function of the command

Description of the syntax

List of arguments and a description of each

List of the possible return values and a description of each

Whether the command is blocking (meaning the command has some external interaction) or nonblocking

Example of how the command can be used

For information about how returns and events are defined in the code, see "Value Definitions" section.


Notes   For the commands that return information, the returnInfo array often contains useful information. However, if the command is returned because the destination (called) party hangs up, then the returnInfo(code) is the failure code.

If you specify an invalid number of arguments for a command, an error will occur and the script will fail.


acceptCall

The acceptCall command is used at the beginning of the main routine in a TCL script. It sends setup acknowledgement, call proceeding, and call connect messages to the incoming call leg. The Gateway is responsible for translating these messages into the appropriate protocol messages (depending on the call leg) and sending them to the caller.

Syntax

acceptCall

Arguments

None

Return Values

None

Blocking or Non Blocking

Non blocking

Example

# Accept a call and then prompt redialer for a pattern
acceptCall
set prompt(url) flash:redialer_tone.au
set prompt(interrupt) true
set patterns(accountAndPW) \\*\\*.+#\\*\\*.+#
set event [promptAndCollect prompt ret