Callback avec Asterisk

Un article de Wiki SOS-ADMIN.

Sommaire

Introduction

Ce qu'on appelle callback, est, comme son nom l'indique, se faire rappeller. L'interet se trouve dans le fait que si vous avez un abonnement illimité (tel que Wengo), et au moins deux lignes, vous pouvez passer des appels depuis n'importe quel fixe, gratuitement.

Comment ca marche

Vous appellez votre asterisk, il vous demande un code, vous lui donnez, vous raccrochez, et Asterisk vous rappelle 15 secondes plus tard. A ce moment la, vous tapez le numero de votre correspondant, il l'appelle, et vous ne payez donc pas la communication !

Il existe aussi une autre possibilité. Certain reseau de telephonie mobile propose un forfait, pour un cout tres faible, vers un seul numero de fixe. La encore, Asterisk peut servir de passerelle. Il faut dans ce cas là la "Presentation du numero".

L'extension

Les messages a enregistrer

Nous aurons besoin de quelques nessages pour rendre notre systeme plus "humain".

Pour les enregistrer, on peut utiliser un programme special, convertir dans le format d'Asterisk, etc... Mais on peut aussi utiliser tout betement Asterisk !

Pour se faire, nous allons creer une petite extension. Dans le contexte de vos telephones, ajoutez :

exten => 2223,1,Answer()
exten => 2223,2,Playback(vm-callsender)
exten => 2223,3,Record(/tmp/rec:gsm|7)
exten => 2223,4,Playback(/tmp/rec)
exten => 2223,5,Hangup()

vm-callsender est un message deja present dans Asterisk, que nous utilisons simplement pour savoir a quel moment parler :)

Le fichier enregistré sera donc /tmp/rec, au format gsm (suffisant). Le 7 signifie d'attendre 7 secondes de blanc avant la fin de l'enregistrement. Pourquoi autant de temps ? Tout simplement parce que nous utiliserons la fonction Read(), qui ne lit les chiffres que durant la lecture d'un message preenregistré. Donc si le message ne dure que 3 secondes, ca sera compliqué.

Apres chaque message, pensez a deplacer le fichier parmis ceux d'Asterisk.

Vous aurez besoin de 3 messages :

  • "Tapez votres code"
  • "Tapez le numero"
  • "Merci" (Ben oui, faut bien etre poli :))

Le mot de passe

Dans le contexte de reception d'appelle, on va donc demander le mot de passe, et le controler. Mais d'abord, il faut regler quelques parametres, sinon il y a quelques probleme avec le DTMF :

exten => <login>,1,SetLanguage(fr)
exten => <login>,2,SIPDtmfMode(inband)
exten => <login>,3,DigitTimeout(4)
exten => <login>,4,ResponseTimeout(5)

Ensuite, on va donc utiliser la fonction Read, pour recuperer le mot de passe, et GotoIf, pour verifier que c'est bien le bon.

exten => <login>,5,Read(Secret,rc-code,4)
exten => <login>,6,GotoIf($[${Secret} = 1234] ? 7 : 11)
exten => <login>,7,Playback(rc-merci)

Les developpeurs reconnaitrons le style du GotoIf, qui envoie donc sur 7 si le passe est bien 1234, sur 11 sinon.

Le Callback

Il faut maintenant faire appeller Asterisk. On ne peut bien sur pas utiliser Dial, car si on raccroche, ca s'arrete, et donc le Dial ne se fera pas. Asterisk prevoit donc une autre solution. Il faut placer dans un dossier special (/var/spool/asterisk/outgoing/) un fichier avec les bons parametres, et wengo effectuera un appel.

Il y a encore un probleme, asterisk verifie ce dossier tres souvent (mais vraiment, plusieurs fois par seconde), donc on ne peut pas editer le fichier directement dans le dossier, sinon il risque d'en prendre que la moitie. Il faut donc editer ailleurs, et faire un mv. Mais la encore, un autre probleme se pose. Asterisk va rappeller trop vite... Asterisk prevoit donc de regarder la date du fichier, si elle est dans le futur, il attendra cette date la (impossible tout de meme dans notre cas, ca gere pas trop les secondes, et comme je veux pas attendre plus d'une minute, on fera autrement).

Voyons deja le fichier :

Channel: SIP/<num>@wengo-out
MaxRetries: 2
RetryTime: 10
WaitTime: 20
Context: Recall
Extension: s
Priority: 1

Le fichier est assez clair pour ne pas avoir besoin d'explication. Juste, pour Context, Extension et Priority, c'est l'endroit ou Asterisk ira apres que le telephone distant ait repondu.

Revenons a notre context. On a donc vu qu'il faut ecrire ce fichier, et attendre 15 secondes avant de le placer dans le dossier outgoing. On peut donc ajouter apres la demande de mot de passe :

exten => <login>,8,system(echo -e "Channel: SIP/${CALLERIDNUM}@wengo-out\\nMaxRetries: 2\\nRetryTime: 10\\nWaitTime: 20\\nContext: Recall\\nExtension: s\\nPriority: 1" > /tmp/recall.call)
exten => <login>,9,system(/etc/asterisk/recall.sh &)
exten => <login>,10,system(echo "${DATETIME} - ${CALLERID} - ${CHANNEL}" >> /tmp/recall.log)
exten => <login>,11,Hangup()

Voici le fichier recall.sh (qui pourrait être dans n'importe quel langage, tant qu'il est executable) :

#!/bin/sh
# on creer un nom de fichier unique pour eviter les ecrasements
fn="/tmp/$$.`date +%s`"
mv /tmp/recall.call $fn
sleep 15
exec mv $fn /var/spool/asterisk/outgoing

Pourquoi en faire un deamon ? Tout simplement pour qu'asterisk ne bloque pas dessus en attendant la fin de l'execution.

Et bien voila, maintenant Asterisk peut nous appeller, mais il reste encore le contexte Recall a créer

Le contexte Recall

Il est d'une simplicité enfantine, je ne le commenterai donc pas :

[Recall]
exten => s,1,SetLanguage(fr)
exten => s,2,SIPDtmfMode(inband)
exten => s,3,DigitTimeout(4)
exten => s,4,ResponseTimeout(5)
exten => s,5,Read(Teltotel,rc-num,10)
exten => s,6,Dial(SIP/${Teltotel}@wengo-out)
exten => s,7,Hangup()

Une autre version proposé par Jerome vous permet d'obtenir une confirmation du numero :

[Recall]
exten => s,1,SetLanguage(fr)
exten => s,2,SIPDtmfMode(inband)
exten => s,3,DigitTimeout(4)
exten => s,4,ResponseTimeout(5)
exten => s,5,Read(Teltotel,beep,10)
exten => s,6,SayDigits(${Teltotel:0:1})
exten => s,7,SayDigits(${Teltotel:1:1})
exten => s,8,SayNumber(${Teltotel:2:2})
exten => s,9,SayNumber(${Teltotel:4:2})
exten => s,10,SayNumber(${Teltotel:6:2})
exten => s,11,SayNumber(${Teltotel:8:2})
exten => s,12,WaitExten

exten => #,1,GoTo(Recall,s,1)
exten => i,1,GoTo(Recall,s,1)
exten => t,1,GoTo(Recall,s,1)

exten => *,1,Dial(SIP/${Teltotel}@wengo-out)
exten => *,2,Hangup() 

Si le numero est le bon, appuyez sur *, sinon # et recommencez :)

Passerelle

Vous vous souvenez qu'au debut de l'article, on a parlé d'une solution depuis un telephone portable. Il suffit de mettre dans le contexte de reception d'appel :

exten => <login>/<votre_num_tel>,1,Goto(Recall,s,1)

Comme Asterisk est sur que l'appelle vient bien de votre telephone, il n'est pas necessaire de passer par la demande de mot de passe.

Recapitulatif

Voici la totalité du code :

[entrant]
exten => <login>,1,SetLanguage(fr)
exten => <login>,2,SIPDtmfMode(inband)
exten => <login>,3,DigitTimeout(4)
exten => <login>,4,ResponseTimeout(5)
exten => <login>,5,Read(Secret,rc-code,4)
exten => <login>,6,GotoIf($[${Secret} = 1234] ? 7 : 11)
exten => <login>,7,Playback(rc-merci)
exten => <login>,8,system(echo -e "Channel: SIP/${CALLERIDNUM}@wengo-out\\nMaxRetries: 2\\nRetryTime: 10\\nWaitTime: 20\\nContext: Recall\\nExtension: s\\nPriority: 1" > /tmp/recall.call)
exten => <login>,9,system(/etc/asterisk/recall.sh &)
exten => <login>,10,system(echo "${DATETIME} - ${CALLERID} - ${CHANNEL}" >> /tmp/recall.log)
exten => <login>,11,Hangup()
[Recall]
exten => s,1,SetLanguage(fr)
exten => s,2,SIPDtmfMode(inband)
exten => s,3,DigitTimeout(4)
exten => s,4,ResponseTimeout(5)
exten => s,5,Read(Teltotel,rc-num,10)
exten => s,6,Dial(SIP/${Teltotel}@wengo-out)
exten => s,7,Hangup()

Le fichier /etc/asterisk/recall.sh :

#!/bin/sh
# on creer un nom de fichier unique pour eviter les ecrasements
fn="/tmp/$$.`date +%s`"
mv /tmp/recall.call $fn
sleep 15
exec mv $fn /var/spool/asterisk/outgoing