mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1226 lines
38 KiB
1226 lines
38 KiB
/*
|
|
* $Id: IVR_Documentation.txt,v 1.7 2005/01/05 17:18:05 sayer Exp $
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* This file is part of sems, a free SIP media server.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
SEMS plug-in documentation:
|
|
The Interactive Voice Response (IVR) module
|
|
|
|
index
|
|
1. Introduction to the new IVR module
|
|
2. Installation
|
|
2.1 Prerequisites
|
|
2.2 adapting the Makefile
|
|
2.2.1 Script Language
|
|
2.2.2 TTS
|
|
2.2.3 ASTERISK_COMPATIBLE
|
|
2.3 Making the IVR module
|
|
2.4 Run time configuration: sems.conf
|
|
3. General operation
|
|
3.1 How the playlist works
|
|
3.2 Recording
|
|
3.3 How event controlled scripts work
|
|
3.4 Maintaining state in event controlled scripts
|
|
3.5 Text to Speech in the IVR
|
|
3.6 sleep functions
|
|
3.7 redirecting a call (REFER)
|
|
3.8 placing a new call: dialout
|
|
4. Example scripts
|
|
4.1 BeatBox (Python and Perl)
|
|
4.2 Restricted Redirect: ID and PIN verification and redirection
|
|
4.3 voicemail script: send email from SIP call
|
|
4.4 print call information
|
|
5. IVR Function reference
|
|
6. Troubleshooting & FAQ
|
|
7. Contact the authors
|
|
|
|
revision history:
|
|
05.01.05 ssa added tw_append in 3.7, asterisk_compatible 2.2.3
|
|
25.10.04 ssa added 3.7 refer
|
|
26.10.04 ssa changed 3.7 refer cb parameters
|
|
|
|
1. Introduction to the new IVR module
|
|
|
|
The IVR plug-in for SEMS is a general purpose application platform for
|
|
the Sip Express Media Server SEMS. It can be scripted in either Python
|
|
or Perl language to control the Media Server, and thus can be used for
|
|
rapid development and deployment of SIP telephony services.
|
|
|
|
In order to be a platform for the creation of powerful real life
|
|
applications, the ivr module for SEMS has been rewritten to use a playlist
|
|
and support event controlled operation. For more simple applications,
|
|
sequential script can be used as well.
|
|
|
|
It is not recommended to mix sequential and event controlled operation
|
|
though. Especially the sequential functions like ivr.play() may not work
|
|
as expected.
|
|
|
|
Perl has been introduced to the IVR module by Richard at Comtel, and can be
|
|
selected as the scripting language at compile time.
|
|
|
|
2. Installation
|
|
|
|
2.1 Prerequisites
|
|
|
|
* SEMS source distribution
|
|
There is no binary distribution of the IVR yet, as script language and
|
|
text to speech support has to be selected at compile time. The current
|
|
source code for SEMS can be obtained via CVS with the following command:
|
|
$ cvs -d:pserver:anonymous@cvs.berlios.de:/cvsroot/sems co answer_machine
|
|
|
|
* Script interpreter
|
|
For Python: Python >= ver. 2.2 must be installed. The current release can
|
|
be obtained from http://www.python.org ,
|
|
e.g. http://www.python.org/ftp/python/2.3.4/Python-2.3.4.tar.bz2 .
|
|
It is recommended to recompile and install Python from the source package.
|
|
|
|
For Perl: The Perl support requires perl version 5.8.0 and above. It's tested
|
|
in 5.8.3 in Debian test branch. If you successfully run other configurations,
|
|
please give us some feedback so it can be listed here.
|
|
|
|
* Text to Speech (TTS), optional
|
|
If TTS is to be used, the flite speech synthesizer has to be downloaded and
|
|
installed, from http://www.speech.cs.cmu.edu/flite/download.html
|
|
|
|
2.2 adapting the Makefile
|
|
|
|
Before compiling, the Makefile of the IVR plug-in has to be adapted: Script
|
|
Language, Text To Speech Support and Paths.
|
|
|
|
The Makefile for the IVR plug-in is located in answer_machine/plug-in/ivr
|
|
subdirectory of the SEMS source tree.
|
|
|
|
2.2.1 Script Language
|
|
|
|
SCRIPT = <script language>
|
|
|
|
<script language> can one of [Python, Perl, Perl-].
|
|
|
|
For Python:
|
|
If Python 2.2 is used, change PYTHON_VERSION:
|
|
PYTHON_VERSION=2.2
|
|
If the Python headers and libraries are located somewhere else then in the
|
|
default path ( /usr/include/python2.2 and /usr/lib/python2.2 ), change
|
|
PYTHON_PREFIX, e.g. for SuSE:
|
|
PYTHON_PREFIX=/usr/local
|
|
Modules that are by default dynamically loaded, i.e. all modules from
|
|
lib-dynload, must be linked statically to the ivr module. If you use e.g. the
|
|
time module, add to
|
|
PYTHON_DYNLOAD_MODULES = $(PYTHON_LIBDIR)/lib-dynload/time.so
|
|
For the mysql module, link _mysql as well
|
|
PYTHON_DYNLOAD_MODULES = $(PYTHON_LIBDIR)/lib-dynload/time.so,\
|
|
$(PYTHON_LIBDIR)/lib-dynload/_mysql.so
|
|
|
|
Perl:
|
|
Modules that are by default dynamically loaded must be linked statically to the
|
|
ivr module. To facilitate this, there is the option SCRIPT=Perl- which links
|
|
commonly used modules.
|
|
|
|
2.2.2 TTS
|
|
If Text to speech is to be used, set
|
|
TTS = y
|
|
FLITE_DIR should point to the root of the flite source distribution.
|
|
|
|
2.2.3 ASTERISK_COMPATIBLE
|
|
|
|
Apparently Asterisk uses the incoming RTP packets as timing source for rtp
|
|
it sends. The ivr can send empty packets (all zeroes) when it is idle (no
|
|
media to play) so voice can be recorded if e.g. asterisks pstn-gw is used.
|
|
|
|
In the Makefile set ASTERISK_COMPATIBLE=y if you want to enable that.
|
|
|
|
2.3 Making the IVR module
|
|
'make' in plug-in/ivr directory should make the IVR module. If it was
|
|
successfully built, there is answer_machine/lib/apps/ivr.so .
|
|
|
|
2.4 Run time configuration: sems.conf
|
|
|
|
The following section is from the sems.conf sample configuration:
|
|
|
|
# sample ivr module configuration (inline)
|
|
config.ivr=inline
|
|
#
|
|
#
|
|
# The IVR checks for a script with the named of the callee
|
|
# (<local part in r-uri>.py for python, <local part in r-uri>.pl for perl)
|
|
# in the directory <ivr_script_path><domain>, then for
|
|
# <ivr_script_path><domain><ivr_script_file>. If this is not found,
|
|
# <ivr_script_path>/<local part in r-uri>.py if searched,
|
|
# and if this is not found, <ivr_script_path>/<ivr_script_file> is used.
|
|
#
|
|
# So with a call to sayer@iptel.org and
|
|
#ivr_script_path=/etc/ivr and
|
|
#ivr_script_file=ivr.py
|
|
# these files are checked:
|
|
#/etc/ivr/iptel.org/sayer.py
|
|
#/etc/ivr/iptel.org/ivr.py
|
|
#/etc/ivr/sayer.py
|
|
#/etc/ivr/ivr.py
|
|
#
|
|
#parameter: ivr_script_path:
|
|
# path to ivr scripts.
|
|
#
|
|
ivr_script_path=/etc/ivr/
|
|
# default script file: this will be executed if <user>.py does not exist.
|
|
#
|
|
ivr_script_file=ivr.py
|
|
|
|
# parameter : tts_caching
|
|
# y or n
|
|
# text will be read from waves already synthesized and
|
|
# cached in cache_path
|
|
tts_caching=y
|
|
|
|
# parameter : tts_cache_path
|
|
# path to cache waves
|
|
# path must exist!
|
|
tts_cache_path=/tmp/wavs
|
|
# end of configuration section for ivr module
|
|
config.ivr=end
|
|
|
|
3. General operation
|
|
|
|
The Python or perl interpreter is embedded in the IVR plug-in. Executing the script
|
|
by its own (e.g. /usr/bin/python ivr.py ) does not make a lot of sense.
|
|
|
|
If a call is transferred to the IVR plug-in, it will start a new instance of a script
|
|
interpreter, which searches for the appropriate script (see above, 2.4) and executes
|
|
it. The script can now play back a file to the user, record the voice of the called,
|
|
collect key inputs, refer the call etc. by calling functions of the ivr module.
|
|
|
|
Example scripts are given in Python here, for example perl scripts have a look at
|
|
section 4.
|
|
|
|
In Python, the ivr module has to be imported first, a simple announcement could look
|
|
like this
|
|
-------- simple announcement script
|
|
import ivr
|
|
ivr.play("hello.wav")
|
|
--------
|
|
|
|
When the script finishes, the call is terminated. A call to this script
|
|
-------- 10 seconds recording
|
|
import ivr
|
|
ivr.startRecording("recorded.wav")
|
|
ivr.sleep(10)
|
|
---------
|
|
will last less then or equal 10 seconds.
|
|
|
|
3.1 How the playlist works
|
|
|
|
Files can be added to the playlist, either at the front or at the back, using
|
|
the ivr.enqueueMediaFile(filename, front) function. By default it is assumed
|
|
that the file should be added to the front of the paylist and played back
|
|
instantly. If there is a file currently played, its playback is interrupted
|
|
until the new file is finished, then its playback is continued from the position
|
|
where it was interrupted.
|
|
|
|
If the playlist should be cleared, e.g. before adding a new file if one doesn't
|
|
want to have the current file continued after the new file, ivr.emptyMediaQueue()
|
|
can be called.
|
|
|
|
3.2 Recording
|
|
|
|
ivr.startRecording(filename) and ivr.stopRecording() are used to record to a
|
|
file. If the extension ".mp3" is given and the MP3 file writer plug-in is there, a
|
|
mp3 file is written. If not, a PCM 16 bit WAV file is written.
|
|
|
|
3.3 How event controlled scripts work
|
|
In an IVR application, the script programmer often wants the IVR doing
|
|
something while waiting for e.g. a keypress of the caller. For example a
|
|
message has to be recorded while the script waits for a keypress by the
|
|
caller or if the caller did not press "next", "last" or "delete" key within
|
|
five seconds, the script could play a help message.
|
|
|
|
The script can register callback functions, which are executed if some event
|
|
occurs. Namely, these are
|
|
|
|
onDTMF key is pressed
|
|
onMediaQueueEmpty playlist is empty
|
|
onBye caller hanged in
|
|
onReferStatus status notification of REFER process (see 3.7)
|
|
|
|
For example these functions can be registered like this:
|
|
|
|
def onDTMF_Func(key):
|
|
print "DTMF: ", key
|
|
|
|
def onMQE_Func():
|
|
print "Playlist ran out!"
|
|
|
|
def onBye_Func():
|
|
print "onBye!"
|
|
|
|
ivr.setCallback(onDTMF_Func, "onDTMF")
|
|
ivr.setCallback(onMQE_Func "onMediaQueueEmpty")
|
|
ivr.setCallback(onBye_Func, "onBye")
|
|
|
|
Note that the onDTMF callback is (so far) the only one with a parameter,
|
|
which holds the key pressed.
|
|
|
|
A simple script that will record 10 seconds or until a key is pressed could
|
|
look like this:
|
|
|
|
-------- record 10 seconds or until key is pressed
|
|
import ivr
|
|
|
|
def onDTMF_Func(key):
|
|
print "DTMF: ", key
|
|
ivr.stopRecording()
|
|
ivr.wakeUp()
|
|
|
|
ivr.setCallback(onDTMF_Func, "onDTMF")
|
|
ivr.enableDTMFDetection()
|
|
ivr.startRecording("recorded.wav")
|
|
ivr.sleep(10)
|
|
---------
|
|
|
|
Note that indentation (TABs at the beginning of the line) matters in Python language.
|
|
|
|
If a message should be played at the end, the script could be modified like this:
|
|
|
|
-------- record 10 seconds or until key is pressed, and then play message
|
|
import ivr
|
|
|
|
def onDTMF_Func(key):
|
|
print "DTMF: ", key
|
|
ivr.stopRecording()
|
|
ivr.wakeUp()
|
|
|
|
def onMediaQueueEmpty_Func():
|
|
ivr.wakeUp()
|
|
|
|
ivr.setCallback(onDTMF_Func, "onDTMF")
|
|
ivr.setCallback(onMediaQueueEmpty_Func, "onMediaQueueEmpty")
|
|
ivr.enableDTMFDetection()
|
|
ivr.startRecording("recorded.wav")
|
|
ivr.sleep(10)
|
|
ivr.say("Thank you for your message")
|
|
ivr.sleep(40)
|
|
# sleep until wakeUp
|
|
---------
|
|
|
|
3.4 Maintaining state in event controlled scripts
|
|
|
|
This script illustrates how to use menus by maintaining state.
|
|
|
|
----- incomplete mailbox script
|
|
import ivr
|
|
|
|
class IvrState:
|
|
def __init__(self):
|
|
self.state = "init_state"
|
|
|
|
def onDTMF(self, key):
|
|
print "onDTMF, key = ", key
|
|
if self.state == "init_state":
|
|
if key == 1:
|
|
self.state = "next_message"
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("the next message:")
|
|
ivr.enqueueMediaFile(my_message.wav, false)
|
|
|
|
elif key == 2:
|
|
self.state = "setup_state"
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("press one to enter your name, two to leave the setup")
|
|
|
|
if self.state == "setup_state":
|
|
if key == 1:
|
|
self.state = "recording_state"
|
|
ivr.startRecording("myname.wav")
|
|
|
|
print "IVR"
|
|
print "IVIVI"
|
|
|
|
s = IvrState()
|
|
|
|
def onDTMF_m(key):
|
|
s.onDTMF(key)
|
|
|
|
ivr.setCallback(onDTMF_m, "onDTMF")
|
|
|
|
ivr.enableDTMFDetection()
|
|
ivr.say("welcome to your mailbox.");
|
|
ivr.sleep(60)
|
|
---------
|
|
|
|
|
|
3.5 Text to Speech in the IVR
|
|
|
|
If the IVR is compiled with flite-TTS enabled, the function ivr.say(message, front)
|
|
can be called. For example you could write
|
|
ivr.say("This is your mailbox")
|
|
To use Text to Speech, you have to download the flite speech synthesizer and uncomment
|
|
two lines in the Makefile in the IVR directory. For more information about installing
|
|
the flite speech synthesizer have a look at answer_machine/plug-in/semstalkflite/Readme.txt .
|
|
|
|
3.6 sleep functions
|
|
|
|
The ivr.sleep(n) and ivr.usleep(n) functions can be used to wait n seconds or microseconds,
|
|
or until ivr.wakeUp() is called. Do not call the sleep() and usleep() functions in callbacks!
|
|
ivr.wakeUp() can be used safely in callback functions, e.g. to wake up the script if some
|
|
event has occured.
|
|
|
|
3.7 redirecting a call (REFER)
|
|
|
|
With the ivr function ivr.redirect(string to_uri) the caller can be redirected
|
|
to a SIP URL. The function call returns the cseq number for the REFER which
|
|
can be used in the script to distinguish the response, if several REFERs are
|
|
made. Depending on the caller, this REFER can be accepted or not, and
|
|
while the call transfer is in progress, the caller sends notifications
|
|
about the status of the redirection via NOTIFY. To enable the script to react
|
|
appropriately to this progress, a callback is introduced:
|
|
|
|
onReferStatus(type, cseq, code_subscriptionstate, reason_status)
|
|
|
|
This callback is used to convey the script _both_ the response to the REFER
|
|
_and_ the notifications given by the caller. The first parameter, "type" holds
|
|
the type of the Status update: "Response" or "Notification". The second
|
|
parameter, cseq (type number), holds the sequence number of the corresponding
|
|
REFER; it is needed to distinguish responses if there is more than one REFER
|
|
in one dialog. The third parameter, "code_subscriptionstate", holds either the
|
|
response code to the REFER, if it is a "Response", or the subscription state
|
|
of the implicit subscription to the REFER status. reason_status for "Response"
|
|
holds the reason string (can be empty), and for "Notification" it is the REFER
|
|
status (i.e. the body of the NOTIFY message).
|
|
|
|
Example script (python):
|
|
def onReferStatus(type, cseq, code_subscriptionstate, reason_status):
|
|
print "onReferStatus... type = " + type
|
|
if type == "Response":
|
|
print "cseq = " + str(cseq)
|
|
print "code = " + str(code_subscriptionstate)
|
|
print "reason = " + reason
|
|
else: # assuming "Notification"
|
|
print "cseq = " + str(cseq)
|
|
print "SubscriptionState: " + code_subscriptionstate
|
|
print "Refer Status: " + reason_status + "\n"
|
|
|
|
ivr.setCallback(onReferStatus, "onReferStatus")
|
|
ivr.say("please wait while your call is being transferred.")
|
|
print "cseq of redirect: "+ str(ivr.redirect("sip:refer2@example.iptel.org"))
|
|
ivr.sleep(20)
|
|
|
|
Note that the NOTIFY message must be handled correctly in ser routing script
|
|
and sent to sems with t_write_unix (or t_write_req), so SEMS will receive it:
|
|
|
|
modparam( "tm", "tw_append",
|
|
"notify_body:hdr[Event];hdr[Subscription-State];msg[body]")
|
|
|
|
/* ... */
|
|
// request routing section
|
|
|
|
if (uri=~"sip:ivr+*@*") {
|
|
if (method == "NOTIFY") {
|
|
if(!t_write_unix("/tmp/am_sock","ivr/notify_body")){
|
|
log("could not contact ivr\n");
|
|
t_reply("500","could not contact ivr");
|
|
};
|
|
} else {
|
|
if(!t_write_unix("/tmp/am_sock","ivr")){
|
|
log("could not contact ivr\n");
|
|
t_reply("500","could not contact ivr");
|
|
};
|
|
};
|
|
break;
|
|
};
|
|
|
|
|
|
|
|
3.8 placing a new call: dialout
|
|
|
|
with the function
|
|
ivr.dialout(string user, string app_name, string uri, string from_user)
|
|
|
|
A new call will be placed. app_name is the SEMS application that gets the new
|
|
call. If the app_name is "ivr" the ivr will get the new call. It will be made to uri from from_user.
|
|
|
|
4. Example applications
|
|
|
|
4.1 BeatBox (Python and Perl)
|
|
|
|
This not very useful application (used to test the playlist ;) will maybe illustrate the
|
|
concepts of playlist and callbacks. Also how to maintain state information using a dialog
|
|
class is probably useful to script programmers.
|
|
|
|
The user is greeted with intro.wav . To every key a wav file is associated (0.wav .. 9.wav).
|
|
If the user enters two times 0 in a row, a different set of files is used (b0.wav .. b11.wav).
|
|
After a maximum of 60 seconds the script hangs up.
|
|
|
|
-- bbox.py ------------------------------------
|
|
import ivr
|
|
import time
|
|
|
|
class BeatBox:
|
|
def __init__(self):
|
|
self.number_path = "/home/ssa/ivr/bbox/numbers/"
|
|
self.bbox_path = "/home/ssa/ivr/bbox/"
|
|
self.lastchar = -1
|
|
self.bbox_mode = 0
|
|
|
|
def onDTMF(self, key):
|
|
print "onDTMF, key = ", key
|
|
print "lastchar = ", self.lastchar
|
|
if self.lastchar == 0 and key == 0 :
|
|
if not self.bbox_mode :
|
|
ivr.enqueueMediaFile(self.bbox_path+"enter_bbox.wav")
|
|
else:
|
|
ivr.enqueueMediaFile(self.bbox_path+"left_bbox.wav")
|
|
self.bbox_mode = not self.bbox_mode
|
|
if self.bbox_mode:
|
|
ivr.enqueueMediaFile(self.bbox_path + "b"+str(key) +".wav")
|
|
else:
|
|
ivr.enqueueMediaFile(self.number_path + str(key) +".wav")
|
|
self.lastchar = key
|
|
|
|
def onMediaQueueEmpty(self):
|
|
print "media ran out..."
|
|
#ivr.enqueueMediaFile(self.bbox_path + "numbers/0.wav")
|
|
print "IVR"
|
|
print "IVIVI"
|
|
|
|
b = BeatBox()
|
|
|
|
def onDTMF_m(key):
|
|
b.onDTMF(key)
|
|
def onMQE_m():
|
|
b.onMediaQueueEmpty()
|
|
|
|
ivr.setCallback(onDTMF_m, "onDTMF")
|
|
ivr.setCallback(onMQE_m, "onMediaQueueEmpty")
|
|
ivr.startRecording("/home/ssa/ivr/bbox/recorded/" + ivr.getFrom() + " - " + time.ctime() + ".wav")
|
|
ivr.enableDTMFDetection()
|
|
ivr.enqueueMediaFile("/home/ssa/ivr/bbox/intro.wav")
|
|
ivr.sleep(60)
|
|
-- bbox.py - end ------------------------------
|
|
|
|
Note that in Python every class member function get self as the first parameter,
|
|
and indentation matters (blocks go together with the same amount of tabs).
|
|
|
|
Also note that a wrapper function (onDTMF_m, onMQE_m) has to be used as
|
|
callback function, ivr.setCallback(b.onDTMF, "onDTMF") will not work.
|
|
|
|
Perl demo script:
|
|
-- beatbox.pl script ---------------------------------
|
|
#test.pl
|
|
my @keys=();
|
|
use Sys::Syslog qw(:DEFAULT setlogsock);
|
|
|
|
syslog('info', 'this is the beginning');
|
|
my $wav_path = '/var/spool/openums/prompts/';
|
|
ivr::enableDTMFDetection();
|
|
syslog('info', 'setting DTMF callback: '. ivr::setCallback('on_DTMF', 'onDTMF') );
|
|
syslog('info', 'filling media queue: '. ivr::enqueueMediaFile($wav_path . 'aloha.wav', 0));
|
|
|
|
ivr::startRecording('/tmp/record' . ivr::getFrom() . '.wav');
|
|
for ($i=0; $i<10; $i++) {
|
|
$sleep_time = ivr::msleep(5000);
|
|
syslog('info', 'wake up after sleep '. $sleep_time . ' msec.\n');
|
|
if ($sleep_time >= 5000) {
|
|
# look like normal wakeup, no key pressed
|
|
syslog('info', 'no key pressed after 5 sec');
|
|
} else {
|
|
$key = shift (@keys);
|
|
syslog('info', 'A new key '. $key . " is pressed after $sleep_time msec");
|
|
ivr::enqueueMediaFile("$wav_path" . $key . '.wav');
|
|
}
|
|
}
|
|
|
|
syslog('info', 'this is the end');
|
|
|
|
sub on_DTMF {
|
|
push @keys, @_;
|
|
syslog('info', "callback says: a key @_ is entered");
|
|
ivr::wakeUp();
|
|
}
|
|
-------------------------------------------------
|
|
|
|
4.2 Restricted Redirect: ID and PIN verification and redirection (Python)
|
|
|
|
This script asks the caller to enter user ID, PIN and a PSTN number. Before the
|
|
call is redirected to that number, the uid and pin is verified against values
|
|
read from a cvs text file
|
|
|
|
-- restricted_redirect.py ----------------------
|
|
#!/usr/bin/env python
|
|
import ivr
|
|
import csv
|
|
|
|
class IvrDemo:
|
|
def __init__(self):
|
|
self.pin = ""
|
|
self.uid = ""
|
|
self.pstn_nr = ""
|
|
self.dialogstate = "enter_uid"
|
|
self.pinfile = "/usr/local/iptel/conf/pins.csv"
|
|
|
|
def onDTMF(self, key):
|
|
if key < 10:
|
|
ivr.say(str(key), False)
|
|
#print("IVR: User DTMF input key = "+str(key))
|
|
if self.dialogstate == "enter_uid":
|
|
if key == 11:
|
|
self.dialogstate = "enter_pin"
|
|
ivr.say("Now please enter your PIN Number.", False)
|
|
else :
|
|
self.uid += str(key)
|
|
elif self.dialogstate == "enter_pin":
|
|
if key == 11:
|
|
if self.check_pin(self.pin, self.uid):
|
|
self.dialogstate = "enter_pstn"
|
|
ivr.say("Your PIN Number is correct. Please enter then number you want to call.", False)
|
|
print "User entered correct UID/PIN combination "+self.uid+"/"+self.pin
|
|
else:
|
|
ivr.say("I am sorry, your PIN is not correct. Please enter you PIN number again.", False)
|
|
print "User entered wrong UID/PIN combination "+self.uid+"/"+self.pin
|
|
self.dialogstate = "enter_pin"
|
|
self.pin = ""
|
|
else:
|
|
self.pin += str(key)
|
|
elif self.dialogstate == "enter_pstn":
|
|
ivr.emptyMediaQueue()
|
|
if key == 11:
|
|
ivr.redirect(self.pstn_nr)
|
|
self.dialogstate = "waiting_for_redirect"
|
|
ivr.say("You will now be connected to ", False)
|
|
ivr.say(" " + self.pstn_nr, False)
|
|
print "Redirecting user to "+self.pstn_nr
|
|
else :
|
|
self.pstn_nr += str(key)
|
|
elif self.dialogstate == "waiting_for_redirect": pass
|
|
# check if redirect worked
|
|
|
|
def check_pin(self, pin, uid):
|
|
reader = csv.reader(file(self.pinfile))
|
|
for row in reader:
|
|
c_uid = ''
|
|
c_pin = ''
|
|
for i,v in enumerate(row):
|
|
if i == 0:
|
|
c_uid = v
|
|
elif i == 1:
|
|
c_pin = v
|
|
if uid == str(c_uid) and pin == str(c_pin):
|
|
return True
|
|
return False
|
|
|
|
print "IVR"
|
|
print "IVRIVI - Starting session for "+ ivr.getFrom()
|
|
|
|
b = IvrDemo()
|
|
|
|
def onDTMF_m(key):
|
|
b.onDTMF(key)
|
|
|
|
ivr.setCallback(onDTMF_m, "onDTMF")
|
|
ivr.enableDTMFDetection()
|
|
ivr.say("Please enter your User Identification number")
|
|
ivr.sleep(0)
|
|
-- restricted_redirect.py -- end ----------------
|
|
|
|
4.3 voicemail script: send email from SIP call.
|
|
|
|
The caller can either enter a buddy and her or his email address, or send a
|
|
voicemail to one of the buddies. Buddies are stored in a CVS file,
|
|
autocompletion is done if the name of a buddy is only partly entered.
|
|
This script also illustrates how to send a email message with a recorded file
|
|
attached.
|
|
|
|
-- voicemail.py ---------------------------------
|
|
#!/usr/bin/env python
|
|
#/*
|
|
# *
|
|
# * Copyright (C) 2004 Fhg Fokus and Emil Kroymann <kroymann@informatik.hu-berlin.de>
|
|
# *
|
|
# * This file is part of sems, a free SIP media server.
|
|
# *
|
|
# * sems is free software; you can redistribute it and/or modify
|
|
# * it under the terms of the GNU General Public License as published by
|
|
# * the Free Software Foundation; either version 2 of the License, or
|
|
# * (at your option) any later version
|
|
# *
|
|
# * For a license to use the ser software under conditions
|
|
# * other than those described here, or to purchase support for this
|
|
# * software, please contact iptel.org by e-mail at the following addresses:
|
|
# * info@iptel.org
|
|
# *
|
|
# * sems is distributed in the hope that it will be useful,
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# * GNU General Public License for more details.
|
|
# *
|
|
# * You should have received a copy of the GNU General Public License
|
|
# * along with this program; if not, write to the Free Software
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# for smtplib and time, link these modules (see Makefile in plug-in/ivr directory)
|
|
#
|
|
# # put used Python modules from lib-dynload here, e.g. time, mysql etc.
|
|
#PYTHON_DYNLOAD_MODULES = $(PYTHON_LIBDIR)/lib-dynload/time.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/_csv.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/_socket.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/binascii.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/math.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/_random.so \
|
|
# $(PYTHON_LIBDIR)/lib-dynload/cStringIO.so \
|
|
#
|
|
# */
|
|
|
|
|
|
import ivr
|
|
import time
|
|
import csv
|
|
import string
|
|
import smtplib
|
|
# Here are the email pacakge modules we'll need
|
|
from email.MIMEAudio import MIMEAudio
|
|
from email.MIMEMultipart import MIMEMultipart
|
|
|
|
|
|
tel_keys = {
|
|
1 : ['.','_'],
|
|
2 : ['a','b','c'],
|
|
3 : ['d','e','f'],
|
|
4 : ['g','h','i'],
|
|
5 : ['j','k','l'],
|
|
6 : ['m','n','o'],
|
|
7 : ['p','q','r','s'],
|
|
8 : ['t','u','v'],
|
|
9 : ['w','x','y','z'],
|
|
0 : ['@'] }
|
|
|
|
|
|
#d = {'key': 'value', 'key1': 'value1'}
|
|
#l = [1,2,3,4]
|
|
|
|
class ivr_sm:
|
|
def __init__(self):
|
|
self.state = 'main_menu'
|
|
self.last_key = -1
|
|
self.key_index = 0
|
|
self.nickname = ''
|
|
self.email = ''
|
|
self.timeoutvalue = 0
|
|
self.have_typed_key = False
|
|
self.in_session = True
|
|
self.buddylist = { 'sanchez' : 'sayer@cs.tu-berlin.de', }
|
|
self.buddyfile = "buddy.csv"
|
|
self.main_menu_text = "send a voicemail with 1, add a contact with 2, and with 3 I tell you all buddys I know"
|
|
self.vm_filename = ''
|
|
self.auto_enter_key_after = 5
|
|
self.go_back_to_sleep = False
|
|
|
|
##########################
|
|
def onBye(self):
|
|
self.in_session = False
|
|
|
|
def onDTMF(self, key):
|
|
print("voicemail script: DTMF, state = %s" % self.state)
|
|
|
|
if self.state == 'main_menu':
|
|
if key == 1:
|
|
self.state = 'send_voicemail'
|
|
self.nickname = ''
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("Please enter the buddy you want to send a voicemail to.")
|
|
self.timeoutvalue = self.auto_enter_key_after
|
|
ivr.wakeUp()
|
|
if key == 2: # add contact
|
|
self.state = 'add_contact_enter_nickname'
|
|
self.nickname = ''
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("Please tell me the nickname of your new contact")
|
|
self.timeoutvalue = self.auto_enter_key_after
|
|
ivr.wakeUp()
|
|
if key == 3: # tell all contacts
|
|
self.state = 'tell_all_contacts'
|
|
ivr.emptyMediaQueue()
|
|
s = "I know "
|
|
for nick, mailadr in self.buddylist.iteritems():
|
|
s = s + nick + " with email address " + mailadr + ", and I know "
|
|
s = s + " you."
|
|
ivr.say(s)
|
|
##############################################
|
|
elif self.state == 'add_contact_enter_nickname':
|
|
self.have_typed_key = True
|
|
self.reinit_autoenter()
|
|
if key == 11: # finished
|
|
ivr.say("The nickname is %s, now give me the email address please." % self.nickname )
|
|
key = -1
|
|
self.state = 'add_contact_enter_email'
|
|
|
|
elif key == 10: #backspace
|
|
self.nickname = self.nickname[0:-1]
|
|
ivr.say(self.nickname)
|
|
self.last_key = -1
|
|
|
|
elif key != self.last_key:
|
|
if self.last_key == -1:
|
|
self.last_key = key
|
|
self.key_index = 0
|
|
return
|
|
self.nickname += tel_keys[self.last_key][self.key_index]
|
|
ivr.say(self.nickname)
|
|
self.key_index = 0
|
|
self.last_key = key
|
|
|
|
elif key == self.last_key:
|
|
self.key_index += 1
|
|
if self.key_index >= len(tel_keys[self.last_key]):
|
|
self.key_index = 0
|
|
|
|
##############################################
|
|
elif self.state == 'add_contact_enter_email':
|
|
self.reinit_autoenter()
|
|
self.have_typed_key = True
|
|
if key == 11: # finished
|
|
ivr.emptyMediaQueue()
|
|
self.buddylist[self.nickname]=self.email;
|
|
print "writing contacts..."
|
|
writer = csv.writer(file(self.buddyfile, "w"))
|
|
for nick, mailadr in self.buddylist.iteritems():
|
|
writer.writerow((nick, mailadr))
|
|
ivr.say("I have added your buddy %s with email address %s. \
|
|
Now press 1 if you want to send a voicemail and any other key \
|
|
if you want to go back to the main menu" % (self.nickname, self.email))
|
|
self.state = 'finished_enter_contact'
|
|
self.timeoutvalue = 0 # endless
|
|
ivr.wakeUp()
|
|
|
|
elif key != self.last_key:
|
|
if self.last_key == -1:
|
|
self.last_key = key
|
|
self.key_index = 0
|
|
return
|
|
self.email += tel_keys[self.last_key][self.key_index]
|
|
ivr.say(self.email)
|
|
self.last_key = key
|
|
self.key_index = 0
|
|
|
|
elif key == self.last_key:
|
|
self.key_index += 1
|
|
if self.key_index >= len(tel_keys[self.last_key]):
|
|
self.key_index = 0
|
|
##############################################
|
|
elif self.state == 'finished_enter_contact':
|
|
if key == 1:
|
|
self.init_voicemail()
|
|
else:
|
|
self.state = 'main_menu'
|
|
self.timeoutvalue = 0
|
|
ivr.wakeUp()
|
|
ivr.emptyMediaQueue()
|
|
ivr.say(self.main_menu_text)
|
|
|
|
##############################################
|
|
elif self.state == 'tell_all_contacts':
|
|
ivr.emptyMediaQueue()
|
|
self.return_to_main()
|
|
|
|
##############################################
|
|
elif self.state == 'send_voicemail':
|
|
self.have_typed_key = True
|
|
self.reinit_autoenter()
|
|
if key == 11: # finished
|
|
self.nickname = self.find_nick(self.nickname)
|
|
#ivr.say("The nickname is %s." % self.find_nick(self.nickname) )
|
|
key = -1
|
|
self.init_voicemail()
|
|
|
|
elif key == 10: #backspace
|
|
self.nickname = self.nickname[0:-1]
|
|
ivr.say(self.nickname)
|
|
self.last_key = -1
|
|
elif key == 1:
|
|
if len(self.nickname) == 0:
|
|
ivr.say("You did not tell me who you want to call.")
|
|
elif len(self.find_nick(self.nickname)) == 0:
|
|
ivr.say("You asked me if I knew %s, but I don't do." % self.nickname)
|
|
else:
|
|
ivr.say("Do you mean %s ?" % self.find_nick(self.nickname) )
|
|
|
|
elif key != self.last_key:
|
|
if self.last_key == -1:
|
|
self.last_key = key
|
|
self.key_index = 0
|
|
return
|
|
self.nickname += tel_keys[self.last_key][self.key_index]
|
|
s = self.find_nick(self.nickname)
|
|
if len(s) != 0:
|
|
ivr.say(s)
|
|
else:
|
|
ivr.say("I don't know anyone who starts with: %s" %self.nickname)
|
|
|
|
self.key_index = 0
|
|
self.last_key = key
|
|
|
|
elif key == self.last_key:
|
|
self.key_index += 1
|
|
if self.key_index >= len(tel_keys[self.last_key]):
|
|
self.key_index = 0
|
|
|
|
##############################################
|
|
elif self.state == 'before_voicemail':
|
|
if key == 1:
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("beep")
|
|
self.vm_filename = '/home/ssa/ivr/conf/voicemail/rec/'+\
|
|
str(int(time.time())) + '.mp3'
|
|
ivr.startRecording(self.vm_filename)
|
|
self.state = 'recording_voicemail'
|
|
|
|
elif key == 0:
|
|
self.return_to_main()
|
|
##############################################
|
|
elif self.state == 'recording_voicemail':
|
|
ivr.stopRecording()
|
|
ivr.say(('ok, i am sending your message to %s now. Ok, ' % self.nickname)+self.main_menu_text)
|
|
#ivr.enqueueMediaFile(self.vm_filename)
|
|
|
|
# Create the container (outer) email message.
|
|
msg = MIMEMultipart()
|
|
msg['Subject'] = 'voicemail by '+ ivr.getFrom()
|
|
|
|
msg['From'] = 'ssa@fokus.fraunhofer.de'
|
|
msg['To'] = 'ssa@fokus.fraunhofer.de'
|
|
msg.preamble = 'Hello, this is a voicemail sent by ' + ivr.getFrom() + '.'
|
|
# Guarantees the message ends in a newline
|
|
msg.epilogue = ''
|
|
|
|
fp = open(self.vm_filename, 'rb')
|
|
vm_msg = MIMEAudio(fp.read(), _subtype="mp3")
|
|
fp.close()
|
|
msg.add_header('Content-Disposition', 'attachment')
|
|
msg.attach(vm_msg)
|
|
|
|
# Send the email via our own SMTP server.
|
|
s = smtplib.SMTP()
|
|
s.connect()
|
|
print "sending mail to "+ self.buddylist[self.nickname]
|
|
s.sendmail('ssa@fokus.fraunhofer.de', self.buddylist[self.nickname], msg.as_string())
|
|
s.close()
|
|
|
|
self.state = 'main_menu'
|
|
self.timeoutvalue = 0
|
|
ivr.wakeUp()
|
|
|
|
self.return_to_main()
|
|
|
|
def init_voicemail(self):
|
|
ivr.emptyMediaQueue()
|
|
ivr.say("If you are ready to send a voicemail to %s please press 1" % self.nickname)
|
|
self.state = 'before_voicemail'
|
|
self.timeoutvalue = 10
|
|
ivr.wakeUp()
|
|
|
|
def return_to_main(self):
|
|
self.state = 'main_menu'
|
|
self.timeoutvalue = 0
|
|
ivr.say(self.main_menu_text)
|
|
ivr.wakeUp()
|
|
|
|
def find_nick(self, nickpart):
|
|
foundnick = ''
|
|
max_same_chars = 0
|
|
for nick, mailadr in self.buddylist.iteritems():
|
|
if string.count(nick, nickpart) > max_same_chars:
|
|
max_same_chars = string.count(nick, nickpart)
|
|
foundnick = nick
|
|
return foundnick
|
|
|
|
def reinit_autoenter(self):
|
|
#self.timeoutvalue = self.auto_enter_key_after
|
|
self.go_back_to_sleep = True
|
|
ivr.wakeUp()
|
|
|
|
###########################
|
|
def timeoutaction(self):
|
|
print("voicemail script: timeoutaction, state = %s" % self.state)
|
|
if self.state == 'add_contact_enter_nickname':
|
|
if self.last_key == -1:
|
|
return
|
|
if self.have_typed_key:
|
|
self.nickname += tel_keys[self.last_key][self.key_index]
|
|
self.key_index = 0
|
|
ivr.say(self.nickname)
|
|
self.last_key = -1
|
|
self.have_typed_key = False
|
|
|
|
elif self.state == 'send_voicemail':
|
|
if self.last_key == -1:
|
|
return
|
|
if self.have_typed_key:
|
|
self.nickname += tel_keys[self.last_key][self.key_index]
|
|
self.key_index = 0
|
|
ivr.say(self.find_nick(self.nickname))
|
|
self.last_key = -1
|
|
self.have_typed_key = False
|
|
|
|
if self.state == 'add_contact_enter_email':
|
|
if self.last_key == -1:
|
|
return
|
|
if self.have_typed_key:
|
|
self.email += tel_keys[self.last_key][self.key_index]
|
|
self.key_index = 0
|
|
ivr.say(self.email)
|
|
self.last_key = -1
|
|
self.have_typed_key = False
|
|
|
|
if self.state == 'before_voicemail':
|
|
ivr.say('send a voicemail to %s with 1.' % self.nickname)
|
|
|
|
def mainloop(self):
|
|
while self.in_session:
|
|
ivr.sleep(self.timeoutvalue)
|
|
if self.go_back_to_sleep:
|
|
self.go_back_to_sleep = False
|
|
else:
|
|
self.timeoutaction()
|
|
|
|
def onMediaQueueEmpty(self): pass
|
|
|
|
def readbuddies(self):
|
|
print "reading contacts..."
|
|
reader = csv.reader(file(self.buddyfile))
|
|
for row in reader:
|
|
print(row)
|
|
nick = ''
|
|
mail = ''
|
|
for i, v in enumerate(row):
|
|
if i == 0:
|
|
nick = v
|
|
elif i == 1:
|
|
mail = v
|
|
self.buddylist[nick] = mail
|
|
|
|
sm = ivr_sm()
|
|
|
|
def onDTMF_m(key):
|
|
sm.onDTMF(key)
|
|
def onMQE_m():
|
|
sm.onMediaQueueEmpty()
|
|
def onBye_m():
|
|
sm.onBye()
|
|
|
|
sm.buddyfile = "/home/ssa/ivr/conf/voicemail/buddy.csv"
|
|
sm.readbuddies()
|
|
|
|
ivr.setCallback(onDTMF_m, "onDTMF")
|
|
ivr.setCallback(onMQE_m, "onMediaQueueEmpty")
|
|
ivr.setCallback(onBye_m, "onBye")
|
|
|
|
ivr.enableDTMFDetection()
|
|
ivr.say("Hello this is the fantastic voice mailer! " + sm.main_menu_text)
|
|
|
|
sm.mainloop() #
|
|
#ivr.sleep(0) # endless
|
|
-- voicemail.py - end ---------------------------
|
|
|
|
4.4 print call information
|
|
|
|
an example script that prints out all information:
|
|
-----
|
|
print "IVR"
|
|
print "IVRIVI-------- call to "+ivr.getTo()
|
|
print "ivr.getFrom() : " + ivr.getFrom()
|
|
print "ivr.getFromTag() : " + ivr.getFromTag()
|
|
print "ivr.getFromURI() : " + ivr.getFromURI()
|
|
print ""
|
|
print "ivr.getTo() : " + ivr.getTo()
|
|
print "ivr.getToTag() : " + ivr.getToTag()
|
|
print "ivr.getToURI() : " + ivr.getToURI()
|
|
print ""
|
|
print "ivr.getUser() : " + ivr.getUser()
|
|
print "ivr.getEmail() : " + ivr.getEmail()
|
|
print "ivr.getDstIP() : " + ivr.getDstIP()
|
|
print "ivr.getPort() : " + ivr.getPort()
|
|
print "ivr.getCallID() : " + ivr.getCallID()
|
|
print "ivr.getDomain() : " + ivr.getDomain()
|
|
print "ivr.getTime() : " + str(ivr.getTime())
|
|
-------
|
|
|
|
5. IVR Function reference
|
|
It is not recommended to mix the sequential IVR operation with event controlled IVR
|
|
operation ( 2. and 3.). Functions for sequential operations will probably not
|
|
work as expected.
|
|
|
|
5.1. informational
|
|
|
|
* ivr.getTime()
|
|
returns time as int (seconds from epoch)
|
|
* ivr.getFrom()
|
|
return From as string
|
|
* ivr.getTo()
|
|
returns To as string
|
|
* ivr.getFromURI()
|
|
returns From URI as string
|
|
* ivr.getToURI()
|
|
returns To URI as string
|
|
* ivr.getDomain()
|
|
returns Domain as string
|
|
* ivr.getUser()
|
|
returns the User as string
|
|
* ivr.getEmail()
|
|
returns the email address if found in serweb tables
|
|
* ivr.getCallID()
|
|
returns the Call ID
|
|
* ivr.getFromTag()
|
|
return the from Tag
|
|
* ivr.getToTag()
|
|
return the To Tag
|
|
* ivr.getPort()
|
|
return the remote port
|
|
* ivr.getDstIP()
|
|
return the destination IP
|
|
|
|
*ivr.getHeader(string header_name)
|
|
returns the value of the header with given name. Can not be used to
|
|
get the headers above (From, To, ...) !
|
|
ex. print ivr.getHeader('remote-party-id')
|
|
|
|
see 4.4 for an example script that prints out all information.
|
|
|
|
5.2. sequential functions as from the old IVR module:
|
|
* ivr.play(string filename)
|
|
play and wait until the end of the file (queue empty)
|
|
* ivr.record(string filename, int max_rec_time)
|
|
record up to maximum of max_rec_time secs. If max_rec_time = 0, records until
|
|
callee hangs up.
|
|
* int ivr.playAndDetect(string filename)
|
|
play and wait for the end of the file (queue empty) or keypress
|
|
returns pressed key or -1 if no key pressed
|
|
* int ivr.detect([int timeout = 0 (unlimited)])
|
|
detect until timeout occurs.
|
|
Parameter: timeout = 0 : int. If omitted, no timeout (endless) is assumed
|
|
|
|
|
|
5.3. event controlled operation, new IVR functions:
|
|
// setting callbacks
|
|
* ivr.setCallback(Python_function_pointer cb_function, string cb_name)
|
|
|
|
example:
|
|
|
|
def onDTMF_Func(key):
|
|
print "DTMF: ", key
|
|
|
|
def onMQE_Func():
|
|
print "Playlist ran out!"
|
|
|
|
def onBye_Func():
|
|
print "onBye!"
|
|
|
|
ivr.setCallback(onDTMF_Func, "onDTMF")
|
|
ivr.setCallback(onMQE_Func "onMediaQueueEmpty")
|
|
ivr.setCallback(onBye_Func, "onBye")
|
|
|
|
|
|
// Playback and recording
|
|
* ivr.enqueueMediaFile(string filename [, int front = 1 (default true) ])
|
|
|
|
Add a file to the playlist.
|
|
examples:
|
|
ivr.enqueueMediaFile("/tmp/hello.wav")
|
|
#will be added at the front
|
|
|
|
ivr.enqueueMediaFile("/tmp/hello.wav", 0)
|
|
# be added to the back of the playlist
|
|
|
|
* ivr.emptyMediaQueue()
|
|
clear the playlist.
|
|
|
|
*ivr.startRecording(string filename)
|
|
start recording to filename. If already recording, record is closed first.
|
|
|
|
*ivr.stopRecording()
|
|
stop recording. If not recording, does nothing
|
|
|
|
*ivr.say (string msg [, int front = 1 (default true)] )
|
|
*only available if compiled with flite-Text-to-Speech support (see Makefile)*
|
|
|
|
do Text to Speech and enqueue (= put in playlist) result.
|
|
|
|
|
|
// DTMF functions
|
|
* ivr.enableDTMFDetection
|
|
be sure to call setCallback(onDTMF_FUNC, "onDTMF") first!
|
|
|
|
* ivr.disableDTMFDetection()
|
|
disable DTMF detection permanently (i.e. until the next enableDtmfDetection)
|
|
|
|
* ivr. pauseDTMFDetection
|
|
pause DTMF detection temporarily, can be resumed
|
|
* ivr.resumeDTMFDetection
|
|
resume DTMF detection
|
|
|
|
|
|
// bed time
|
|
* ivr.sleep(int n)
|
|
sleep n seconds or until wakeUp() is called
|
|
* ivr.usleep(int n)
|
|
sleep n microseconds or until wakeUp() is called
|
|
* ivr.wakeUp()
|
|
wake sleeping script
|
|
|
|
// call control
|
|
* int ivr.redirect(string to_uri)
|
|
|
|
REFER the caller to to_uri. See section "3.7 redirecting a call
|
|
(REFER)" for details.
|
|
return value: cseq of the REFER command.
|
|
|
|
* ivr.dialout(string user, string app_name, string uri, string from_user)
|
|
|
|
Place a new call, which will be made to uri from from_user. app_name
|
|
is the SEMS application that gets the new call.
|
|
|
|
6. Troubleshooting & FAQ
|
|
|
|
6.1 Q:"I get unresolved symbol XYZ when I run /usr/bin/python ivr.py"
|
|
A: the python/perl interpreter is embedded in the ivr plug-in. Read
|
|
3. General operation.
|
|
|
|
6.2 Q:"I get unresolved symbol XYZ when I do import mysql.so in my script"
|
|
A:link all modules used from lib-dynload with the ivr modules. if you
|
|
use perl, change SCRIPT=Perl to SCRIPT=Perl- , if you use Python, add
|
|
_mysql.so (and some others) to PYTHON_DYNLOAD_MODULES . See "2.2
|
|
Adapting the Makefile".
|
|
|
|
6.3 Q:"I do not get the status of a REFER in my script"
|
|
A:See 3.7 on how to get response and notification of refer method.
|
|
|
|
6.4 Q:"I cannot record from asterisk pstn-gw when no file is played (media
|
|
queue empty)."
|
|
A:see 2.2.3.
|
|
|
|
If you encounter any problems or bugs, please write to the sems mailing list ( sems@iptel.org )
|
|
or directly me at stefan.sayer@fokus.fraunhofer.de .
|
|
|
|
7. Contact the authors
|
|
|
|
Stefan Sayer can best be reached at mailto:stefan.sayer@fokus.fraunhofer.de or
|
|
sip:sayer@iptel.org.
|
|
|
|
Fraunhofer Institut FOKUS Email:stefan.sayer@fokus.fraunhofer.de
|
|
Kaiserin-Augusta-Allee 31 Phone: +49 30 3463 7137
|
|
D-10589 Berlin, Germany
|
|
http://www.fokus.fraunhofer.de/research/cc/mobis
|
|
|
|
Richard can be reached at mailto:richard@o-matrix.org
|
|
|