Wednesday, July 8, 2009

How to read DTMF and store in the database in Asterisk

Aim :

To Read a variable in the form for DTMF tones as pressed by the caller.

For example if you would like your users to call up the system and record there inputs in the database and then make use of Asterisk to perform what ever tasks with those recorded inputs.

Syntax :

Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])

Reads a #-terminated string o
f digits a certain number of times from the user in to the given variable.
  • variable: variable where the user's response will be stored.
  • filename: file to play before reading digits.
  • maxdigits: maximum acceptable number of digits. Stops reading after maxdigits have been entered (without requiring the user press the '#' key). Defaults to 0 - no limit - wait for the user press the '#' key. Any value below 0 means the same. Max accepted value is 255.
  • option: may be 'skip' to return immediately if the line is not up, or 'noanswer' to read digits even if the line is not up.
  • attempts: if greater than 1, that many attempts will be made in the event no data is entered.
  • timeout: Timeout in seconds. If greater than 0, that value will override the default timeout.

Read should disconnect if the function fails or errors out.

[edit]

Example

; The Following Example uses Read and Say digits to read 3 digits from user, saying each one as user types.

Asterisk 1.2:
exten => s,1,Gotoif($[ "${LEN(${extensao})}" < "3"]?3:100)
exten => s,n,NoOp(Executing - ${extensao} - )
exten => s,n,Read(digito||1)
exten => s,n,SayDigits(${digito})
exten => s,n,Set(extensao=${extensao}${digito})
exten => s,n,GoTo(s,1)
exten => s,100,GoTo(from-pstn,s,1)
exten => h,1,hangup()

Asterisk 1.4, 1.6:
exten => s,1(start),Gotoif($[ "${LEN(${extensao})}" < "3"]?collect:pstn)
exten => s,n,NoOp(Executing - ${extensao} - )
exten => s,n(collect),Read(digito,,1)
exten => s,n,SayDigits(${digito})
exten => s,n,Set(extensao=${extensao}${digito})
exten => s,n,GoTo(start)
exten => s,n(pstn),GoTo(from-pstn,s,1)
exten => h,1,hangup()


[edit]

Notes

Pipe characters ( | ) have been changed to commas in Asterisk 1.6.
In Asterisk 1.4, either pipes OR commas will work. Using commas will ease future upgrades.

'#' is the End-Of-Input Key. It is NOT possible to accept '#' with Read()

Warning:
In Asterisk 1.4: the "filename" field does not accept concatenation (file1&file2&file...).

Tuesday, July 7, 2009

Configuring a queue

The following queue works as following:

star all calls are monitored, i.e. saved to disk
star if after 60 seconds on the queue the call is unanswered, the call is routed to voicemail
star there are two levels of agents: agents 302 and 303 will answer the queue (level 1); only if none of them is available the call is routed to agent 301 (level 2). If nobody is available, the queue keeps trying until timeout is reached.

Extensions.conf

[q-my-sample]
; ...queue description.....
exten =>
s,1,SetVar(MONITOR_FILENAME=/var/spool/asterisk/queuecalls/QSAMPLE-${
UNIQUEID})
exten => s,2,Queue(q-sample|n|||)
exten => s,3,Playback(voicemail-invitation)
exten => s,4,VoiceMail,s2001

Queues.conf

[q-sample]
music = default
announce = q-sample-announce
strategy = roundrobin
timeout = 60
retry = 5
maxlen = 0
announce-frequency = 0
announce-holdtime = no
monitor-format = wav
monitor-join = yes
queue-youarenext = silence
queue-thankyou = q-sample-thankyou
member=>Agent/302,0
member=>Agent/303,0
member=>Agent/301,1

Understanding queue logic in Asterisk

When a call enters the queue application, there are many possible scenarios, that are controlled by the queue's own timeout and the ringing timeout.

If a timeout is specified on the queue command itself, that is the maximum queue wait length (we call this queue timeout). It is the maximum time that a caller can wait on hold on the queue. If that time is expired, then the caller is moved on to the next step in the dialplan. If no timeout is specified, we assume it to be the default value, i.e. 300 seconds (5 minutes).

There is then another timeout specified in the queue definition itself, that is the ringing timeout. This is the queue "loop time", i.e. the timeout over which events in the queue are rotated. If the queue finds a free member, it tries to ring it for the given period of time. If it finds no free members, it retries finding a new member when this time period expires. In any case, the global queue timeout is triggered only when this timer expires, so if this timeout is very long, your queue timeouts may be longer than expected. If a member is busy or unavailable (e.g. an extension that is not working) it is skipped as soon as the condition is detected and the next available agent is rung.

If the option "n" is passed to the queue command, there are no retries, i.e. when the retry period expires, the queue is terminated no matter what.

It is possible to add a wait time after an extension is ringed unsuccessfully and the next one is ringed; this parameter id the retry parameter in the queue definition.

When the ringing timeout expires, all queue events are processed, not only the global queue timeout; if an announce is set and the time from the last announce has elapsed, then the announce is played. While the announce is being played, no new agent is being called; this might be a minor problem if your queue-thankyou message is very short, but if it is quite long and quite frequent, you may find an undue slowdown on your agents being searched.

As a summary, we can say that:
star Your ringing timeout should ideally be quite short (10-15 seconds)
star Announce frequencies and the overall queue timeout should be comparatively much longer than the ringing timeout, and are triggered on a schedule driven by the ringing timeout
star The time taken to play announces is added between each ringing timeout cycle, so if it is very long, you may find that your agents sit idle while callers listen to the announcement.

Compressing recorded calls to MP3

If you record a big chunk of traffic (or even all traffic, as some call-centers do) on your Asterisk box, you will see that it ends up taking a substantial amount of disk space. The problem is the following:
star Recording wav files is very good in terms of CPU usage, but those files end up being huge
star Transcoding to GSM files still takes up substantial disk space and is much heavier on the CPU
The ideal would be saving those files in some very-compressed MP3 format, but this is not a very good idea as the hit on the CPU is quite dramatic.
Still, most systems do not operate on a 24/7 basis; if your call center gets a lot of traffic during office hours, it's just as well likely that it will be sitting idle all night.

Therefore, it would be nice if we could:
star Record all calls using the cheapest recording format
star Transform all calls to some very compressed format at night, when the CPU is sitting idle


his way, if you listen to a call that's just been recorded, you will download a large wav file; if you listen to a call that has been recorded two days ago, you will download a highly-compressed MP3 file.

For example, take the standard agent-101 recording file that is distribuited with Asterisk; its size is:

+--------------+----------------+
| wav file | 1310764 bytes |
| gsm file | 135168 bytes |
| mp3 file 16k | 164520 bytes |
| mp3 file 12k | 120528 bytes |
| mp3 file 8k | 83808 bytes |
+--------------+----------------+

The mp3 file is compressed as 16kbps/mono and sounds pretty good, likely better that the gsm file. The mp3 compressed at 8kbps/mono sounds a bit compressed, but it stays intellegible.

Enconding wav files into MP3
To encode wav files into mp3, we create a make file. Make is the right tool for the job, as it is built to transform one file into a different file, by name.

So we edit a file called Makefile (yes, with the capital M) into our main storage directory, usually /var/spool/asterisk/monitor:

DIRWAV = $(shell dir *.wav)
ALLWAV = $(DIRWAV:.wav=.mp3)
DIRGSM = $(shell dir *.gsm)
ALLGSM = $(DIRGSM:.gsm=.mp3)
mp3: $(ALLWAV) $(ALLGSM)
%.mp3: %.wav
nice lame --quiet --preset phone $? $@
rm -f $?
%.wav: %.gsm
nice sox $? -r 8000 -c 1 -w -s $@
rm -f $?
clean:
rm -f *.mp3
rm -f *.wav
rm -f *.raw
rm -f *.gsm
cp /var/lib/asterisk/mohmp3/*.wav .
cp /var/lib/asterisk/sounds/a*.gsm .

(You can omit the clean section we used for testing).

Leave a few blank lines after each target.

Make sure you have sox and lame installed on your machine.

Then at night you run a job like:

cd /var/spool/asterisk/monitor; make mp3

At night, this makefile will search all wav (or gsm) files and convert them into mp3, deleting the original files.

Choosing 16k, 12k or 8k compression
In order to fine-tune the mp3 encoding to your preferred size, you can change the parameter after --preset to:

star --preset phone -> 16kbps/mono, high quality
star --preset 12 -> 12kbps/mono, normal quality
star --preset 8 -> 8 kbps/mono, low quality

If your system is not over-loaded, you may also want to pass the '-h' parameter to turn on high-quality (but slower) mp3 encoding.

Recording of Extensions for Outgoing calls

You want to record all outgoing traffic done by some extensions - not all extensions (it would be trivial) but just some of your choosing. You also want to be able to turn recordings on or off without modifying the dialplan.

How to do it
We will use Asterisk's internal database to record a flag for each extension that tells us whether to record those calls or not. We add a check for this flag before dialling out, so we can catch all outgoing traffic.

Modifying the dialplan
Whenever you are currently dialing out (we assume that any number starting with 0 is an external call) you add the following piece of dialplan:

exten => _0.,1,NoOP,Dial out with hidden CLID
exten => _0.,2,SetCallerPres(prohib)
exten => _0.,3,DBGet(rec=registra/${CALLERIDNUM})
exten => _0.,4,GotoIf($[ ${rec} = 1 ]?10:20)
exten => _0.,10,MixMonitor(REC-${CALLERIDNUM}-${UNIQUEID}.wav|b|)
exten => _0.,11,Goto(20)
exten => _0.,20,Dial(Zap/g1/${EXTEN:1})

Deciding which extensions will be recorded
Once your piece of dialplan is in place, you use the following command from the Asterisk CLI to tell Asterisk an extension is to be recorded:

database put registra 299 1

While you use the following to tell Asterisk an extension is not to be recorded anymore:

database put registra 299 0

If you want to know the status of your recorded extension, you type:

ast*CLI> database show registra
/registra/223 : 1
/registra/224 : 0
/registra/299 : 1

Any key that has value of 1 will be recorded; any other key (whether it has value 0 or does not exist) is not recorded.

Finding recorded calls
You can find the recorded calls by issuing the following command:

[root@ast monitor]# ls -l /var/spool/asterisk/monitor/REC*
-rw-r--r-- 1 root root 186284 Jun 9 15:36 /var/spool/asterisk/monitor/REC-299-1186583777.73726.wav
-rw-r--r-- 1 root root 206764 Jun 9 15:40 /var/spool/asterisk/monitor/REC-299-1186584038.73774.wav

And you can listen to them by copying them over to any audio software.