-
-
-
Creating a Basic IVR Application
-
Application development is very easy on NicIVR/NicSC. State of art call processor allows perfect synchronization between SIP call signals and scripting language. CCS scripting language is more systematic and meaningful than XML based languages.
So developers could implement powerful and flexible applications in a very short time.
Following example demonstrates the relation between a basic IVR application scripts and related signalling states. Please click SIP signals to see the related application code.
IVR Scenario:
1. Caller and Callee registers to NicIVR.
2. The call from caller is received by NicIVR.
3. NicIVR plays "wellcome" and "language selection" announces to caller.
4. Authenticates from billing server and plays the remaining credit to caller.
5. Authorizes from billing server according to called number and obtains the maximum call duration.
6. Starts the call to callee. When the call establishes then starts the session timer for maximum call duration.
7. NicIVR interrupts the call and plays the last minute announce to callee. Then binds the call again.
8. NicIVR ends the call in case of
If maximum duration expires then sends SIP BYE message to both side and ends the call session.
If caller or callee hangs up the phone then NicIVR sends SIP BYE message to other side and ends the call session.
CALLER NICIVR CALLEE
| | |
| | |
|>REGISTER ------------->|<------------- REGISTER<|
| | |
|<--------------- 200 Ok<|>200 Ok---------------->|
| | |
|>INVITE (sdp)---------->| |
| | |
|<----------- 100 TRYING<| |
| | |
|<-----------180 RINGING<| |
| | |
|<----------(sdp) 200 Ok<| |
| | |
|>ACK------------------->| |
| | |
|<=====MEDIA SESSION====>| |
| | |
| |>INVITE (sdp)---------->|
| | |
| |<---------- 180 RINGING<|
| | |
| |<----------(sdp) 200 Ok<|
| | |
| |>ACK ------------------>|
| | |
|<-------(sdp) re-INVITE<| |
| | |
|>200 Ok (sdp)---------->| |
| | |
|<------------------ ACK<| |
| | |
|<================MEDIA SESSION==================>|
| | |
| |>re-INVITE (sdp)------->|
| | |
| |<----------(sdp) 200 Ok<|
| | |
| |>ACK ------------------>|
| | |
|<-------(sdp) re-INVITE<| |
| | |
|>200 Ok (sdp)---------->| |
| | |
|<------------------ ACK<| |
| | |
| | |
|<===LAST MIN WARNING===>| |
| | |
| | |
|<-------(sdp) re-INVITE<| |
| | |
|>200 Ok (sdp)---------->| |
| | |
|<------------------ ACK<| |
| | |
| |>re-INVITE (sdp)------->|
| | |
| |<----------(sdp) 200 Ok<|
| | |
| |>ACK ------------------>|
| | |
|<================MEDIA SESSION==================>|
| | |
|>BYE ------------------>| |
| | |
|<--------------- 20O Ok<| |
| | |
| |>BYE ------------------>|
| | |
| |<--------------- 200 Ok<|
| | |
Main.ccs
// Global variables
string APP_SERVER_RADIUS = "192.168.1.3";
string APP_SERVER_HTTP = "127.0.0.1";
bool g_CallEstablished = FALSE;
string g_strDigits;
string g_strUser;
string g_strPassword;
string g_strh323confid;
string g_strSetupTime;
int iTimerID;
g_CreditAmountInt;
g_CreditAmountDec;
g_nMaxCallDuration;
// main function
FUNCTION main()
{
// generate 32 char random string
// Exp: output 70562F75F241E8A46B07CDD8417F8EF2
g_strh323confid = randHex(32);
LOG("Conf Id : " + g_strh323confid);
// Switch to next script file named NewSession.ccs in Prepaid directory
RunScript("Prepaid\\NewSession.ccs");
}
FUNCTION CloseSession()
{
LOG("CloseSession in Main is interpreting");
LOG("_CLOSING_LEG_ = " + i2str(_CLOSING_LEG_ ));
if (_CLOSING_LEG_ == _LEG_A_) {
LOG("Closing LEG is LEG_A");
EndCall(_LEG_B_);
}
else {
LOG("Closing LEG is LEG_B");
EndCall(_LEG_A_);
}
LOG("-------SESSION_ID:[" + i2str(_SESSION_ID_) + "]---------");
LOG("Session has closed.");
ReleaseSession();
}
FUNCTION AbortSession()
{
EndCall(_LEG_A_);
LOG("-------SESSION_ID:[" + i2str(_SESSION_ID_) + "]---------");
LOG("Session has aborted.");
ReleaseSession();
}
NewSession.ccs
EVENT NewCall()
{
LOG("-------SESSION_ID:[" + i2str(_SESSION_ID_) + "]---------");
LOG("NewCall received. Session has started!");
LOG("Calling Number:" + _CALLING_NR_);
AcceptCall();
AnswerCall();
}
EVENT CallActive()
{
LOG("Call is active");
bool bRet = OpenAudioChannel(_LEG_A_);
if (bRet == FALSE) {
LOG("OpenAudioChannel returned FALSE");
AbortSession();
}
// Play welcome audio
PlayAudio(_LEG_A_, "Welcome");
// Change voice directory for selected language
ChangeLanguage();
// obtaion the balance from billing server
// 8.62 Euro
g_CreditAmountInt = 8;
g_CreditAmountDec = 62;
// Play the amount to caller
PlayAmount();
// Make an authorization request and obtain the maximum call duration
// Check out the sample radius request
g_nMaxCallDuration = 600; // second
LOG("Starting call to: " + _CALLED_NR_);
StartCall(_LEG_A_, _LEG_B_, _CALLED_NR_);
RunScript("Prepaid\\Ringing.ccs");
}
FUNCTION PlayAmount()
{
LOG("Credit Int:" + i2str(g_CreditAmountInt));
LOG("Credit Dec:" + i2str(g_CreditAmountDec));
PlayAudio(_LEG_A_, "SayBalance"); // You have
if (g_CreditAmountInt > 0) {
PlayCredit(_LEG_A_, g_CreditAmountInt, "Say_Euro");
}
if (g_CreditAmountDec > 0) {
PlayCredit(_LEG_A_, g_CreditAmountDec, "Say_Cent");
}
}
FUNCTION ChangeLanguage()
{
// Press 1 for German, 2 French, 3 Spanish
string strDigit = GetDigits(_LEG_A_, 1, "Language_Option");
if (strDigit == "1") {
_LANGUAGE_ = "german";
}
if (strDigit == "2") {
_LANGUAGE_ = "french";
}
if (strDigit == "3") {
_LANGUAGE_ = "spanish";
}
}
EVENT CallEnd()
{
// BYE received
LOG("CallEnd in newsession");
AbortSession();
}
Ringing.ccs
EVENT CallRinging()
{
g_PlayingRinging = TRUE;
PlayBackgroundAudio(_LEG_A_, "Ringing", TRUE, 2);
}
EVENT CallAnswered()
{
LOG("CallAnswered in Ringing is interpreting");
if (g_PlayingRinging == TRUE) {
g_PlayingRinging = FALSE;
StopBackgroundAudio(_LEG_A_);
}
CloseAudioChannel(_LEG_A_);
JoinLegs(_LEG_B_, _LEG_A_);
RunScript("Prepaid\\CallMonitor.ccs");
}
EVENT CallReject()
{
LOG("CallReject in Ringing, Reject Cause: " + i2str(_RESPONSE_));
if (g_PlayingRinging == TRUE) {
g_PlayingRinging = FALSE;
StopBackgroundAudio(_LEG_A_);
}
if (_RESPONSE_ == 404) {
LOG("Called number is not found");
PlayAudio(_LEG_A_, "DestinationNotFound");
}
if (_RESPONSE_ == 486) {
LOG("Called number is busy");
PlayAudio(_LEG_A_, "NumberIsBusy");
}
if ((_RESPONSE_ == 408) || (_RESPONSE_ == 480) ||
(_RESPONSE_ == 484) || (_RESPONSE_ == 487)) {
LOG("Called number does not answer");
PlayAudio(_LEG_A_, "NoAnsver");
}
if (_RESPONSE_ == 500) {
LOG("Interval Server Error (Sip Cause 500)");
PlayAudio(_LEG_A_, "ErrorMessage");
}
AbortSession();
}
EVENT CallEnd()
{
LOG("CallEnd in Ringing is interpreting");
if (g_PlayingRinging == TRUE) {
StopBackgroundAudio(_LEG_A_);
}
CloseSession();
}
CallMonitor.ccs
EVENT CallEstablished()
{
LOG("CallEstablised in CallMonitor is interpreting");
if (g_CallEstablished == TRUE) {
RunScript("Prepaid\\CallMonitor.ccs");
}
int nRet = 0;
g_Pound = FALSE;
g_IsTimerExpired = FALSE;
g_strConnectTime = GetTime();
g_CallEstablished = TRUE;
LOG("Connect Time :" + FormatTime(g_strConnectTime, "%H:%M:%S.000 UTC %a %b %d %Y"));
if (g_CreditTime >= 60) {
LOG("First 60sec setted..." );
g_iTimerId = StartTimer(g_CreditTime - 60, "LastMinWarning", TRUE);
}
else {
g_iTimerId = StartTimer(g_CreditTime, "TimerExpired", TRUE);
}
LOG("Timer Active");
}
EVENT LastMinWarning()
{
LOG("LastMinWarning occured");
RunScript("Prepaid\\LastMinuteWarning.ccs", "UserEvent");
}
EVENT TimerExpired()
{
LOG("TimerExpired in CallMonitor is interpreting");
string strTime = GetTime();
LOG("Time: " + FormatTime(strTime, "%H:%M:%S.000 UTC %a %b %d %Y"));
// Close Both LEG
EndCall(_LEG_IVR_);
//RadiusStopAccounting();
ReleaseSession();
}
EVENT LastMinCallback()
{
LOG("LastMinCallback is interpreting");
if (g_iTimerId != 0) {
LOG("Old Timer stopping." + i2str(g_iTimerId));
StopTimer(g_iTimerId);
g_iTimerId = StartTimer(60, "TimerExpired", TRUE);
}
RunScript("Prepaid\\CallMonitor.ccs");
}
EVENT CallReject()
{
LOG("CallReject in Ringing is interpreting");
LOG("Reject Cause: " + i2str(_RESPONSE_));
if (g_CallEstablished == TRUE) {
LOG("Reject message for re-INVITE ");
EndCall(_LEG_B_);
}
if (_RESPONSE_ == 404) {
LOG("Called number is not found");
PlayAudio(_LEG_A_, "DestinationNotFound");
}
if (_RESPONSE_ == 486) {
LOG("Called number is busy");
PlayAudio(_LEG_A_, "NumberIsBusy");
}
if ((_RESPONSE_ == 408) || (_RESPONSE_ == 480) ||
(_RESPONSE_ == 484) || (_RESPONSE_ == 487)) {
LOG("Called number does not answer");
PlayAudio(_LEG_A_, "NoAnsver");
}
if (_RESPONSE_ == 500) {
LOG("Interval Server Error (Sip Cause 500)");
PlayAudio(_LEG_A_, "ErrorMessage");
}
AbortSession();
}
EVENT CallEnd()
{
LOG("CallEnd in CallMonitor is interpreting");
//RadiusStopAccounting();
CloseSession();
}
EVENT OnDigit()
{
LOG("OnDigit in CallMonitor is interpreting");
SendDigit(_LEG_B_, _DIGIT_, _DIGIT_DURATION_);
}
LastMinuteWarning.ccs
EVENT UserEvent()
{
LOG("LastMinWarning::UserEvent is interpreting");
JoinLegs(_LEG_IVR_, _LEG_B_);
JoinLegs(_LEG_IVR_, _LEG_A_);
bool bRet = OpenAudioChannel(_LEG_A_);
if (bRet == FALSE) {
LOG("LastMinWarning::OpenAudioChannel returned FALSE");
}
// Make the last minute announcement
PlayAudio(_LEG_A_, "YourLast60Sec");
CloseAudioChannel(_LEG_A_);
JoinLegs(_LEG_B_, _LEG_A_);
JoinLegs(_LEG_A_, _LEG_B_);
}
EVENT CallEstablished()
{
LOG("CallEstablised in LastMinWarning is interpreting");
RunScript("Prepaid\\CallMonitor.ccs", "LastMinCallback");
}
EVENT CallEnd()
{
LOG("CallEnd in CallMonitor is interpreting");
//RadiusStopAccounting();
CloseSession();
}
Session Controller Logic
Call signalling is a bit different for NicSC SIP Session Controller. It does not take over the call and It behaves like a proxy. It does not have the capability of playing voice files like NicIVR but signalling-scripting integration seems like NicIVR.
CALLER NICSC CALLEE
| | |
| | |
|>REGİSTER ------------->|<------------- REGISTER<|
| | |
|<--------------- 200 Ok<|>200 Ok---------------->|
| | |
|>INVITE (sdp)---------->| |
| | |
|<----------- 100 TRYING<| |
| | |
| |>INVITE (sdp)---------->|
| | |
| |<---------- 180 RINGING<|
| | |
|<-----------180 RINGING<| |
| | |
| |<----------(sdp) 200 Ok<|
| | |
| |>ACK ------------------>|
| | |
|<----------(sdp) 200 Ok<| |
| | |
|>ACK------------------->| |
| | |
|>re-INVITE (sdp)------->| |
| | |
|<----------- 100 TRYING<| |
| | |
| | |
| |>re-INVITE (sdp)------->|
| | |
| |<----------(sdp) 200 Ok<|
| | |
| |>ACK ------------------>|
| | |
|<----------(sdp) 200 Ok<| |
| | |
|>ACK------------------->| |
| | |
| |<------------------ BYE<|
| | |
| |>20O Ok --------------->|
| | |
|<------------------ BYE<| |
| | |
|>200 Ok --------------->| |
Please contact us for any question or further details: info@creacode.com.tr
-
-
-