Asterisk AGI-Skript in Python für die Rufweiterleitung mit CallThrough

14. Februar 2023

Wird dieses AGI-Skript für Python 2.7 oder Python 3 durch einen Telefonanruf aufgerufen, wird der Anrufer durch eine Sound-Datei aufgefordert seine gewünschte Zielnummer in seine Telefontastatur einzugeben. Das Skript wartet maximal vier Sekunden, bis die nächste Ziffer eingegeben wird. Wenn die 4 Sekunden ohne eine Aktion des Anrufers verstrichen sind, wird die bisher eingetippte Abfolge von Ziffern als Zahl an Asterisk übergeben, um damit in diesem Fall eine Nummer zu wählen. Es dient also dazu CallThrough zu ermöglichen. Dieser Artikel stellt auch eine Alternative für Callthrough ohne AGI-Skripte vor.

Mein Asterisk-SIP-Server ist zum Beispiel extern über eine Nummer von Iptel erreichbar. Wählt jemand aus dem Netz von Iptel diese Nummer, wird er mit diesem AGI-Skript verbunden. Er kann dann nach der Aufforderung “Please enter the number you wish to call” oder “Bitte geben Sie ihre gewünschte Zielnummer” ein die Nummer eintippen und wird nach 4 Sekunden Wartezeit verbunden, was ebenfalls durch ein weiteres Soundfile angekündigt wird. Wird es versäumt irgendeine Ziffer einzugeben, erfolgt nach 4 Sekunden eine Fehlermeldung per Soundfile. Die 4 Sekunden Wartezeit lassen sich im Code leicht abändern Hier der Python-Skript, das ich call.py genannt habe. Ich habe es auf Python 2.7 getestet und müsste auch auf Python 3 laufen.

#!/usr/bin/python

import sys
import re
import time

env = {}
tests = 0;

while 1:
   line = sys.stdin.readline().strip()

   if line == '':
      break
   key,data = line.split(':')
   if key[:4] != 'agi_':
      #skip input that doesn't begin with agi_
      sys.stderr.write("Did not work!\n");
      sys.stderr.flush()
      continue
   key = key.strip()
   data = data.strip()
   if key != '':
      env[key] = data

sys.stderr.write("AGI Environment Dump:\n");
sys.stderr.flush()
for key in env.keys():
   sys.stderr.write(" -- %s = %s\n" % (key, env[key]))
   sys.stderr.flush()

def checkresult (params):
   params = params.rstrip()
   if re.search('^200',params):
      result = re.search('result=(\d+)',params)
      if (not result):
         sys.stderr.write("FAIL ('%s')\n" % params)
         sys.stderr.flush()
         return -1
      else:
         result = result.group(1)
         #debug("Result:%s Params:%s" % (result, params))
         sys.stderr.write("PASS (%s)\n" % result)
         sys.stderr.flush()
         return result
   else:
      sys.stderr.write("FAIL (unexpected result '%s')\n" % params)
      sys.stderr.flush()
      return -2

def sayit (params):
   sys.stderr.write("STREAM FILE %s \"\"\n" % str(params))
   sys.stderr.flush()
   sys.stdout.write("STREAM FILE %s \"\"\n" % str(params))
   sys.stdout.flush()
   result = sys.stdin.readline().strip()
   checkresult(result)


def getnumber (prompt, timelimit, digcount):
   sys.stderr.write("GET DATA %s %d %d\n" % (prompt, timelimit, digcount))
   sys.stderr.flush()
   sys.stdout.write("GET DATA %s %d %d\n" % (prompt, timelimit, digcount))
   sys.stdout.flush()
   result = sys.stdin.readline().strip()
   result = checkresult(result)
   sys.stderr.write("digits are %s\n" % result)
   sys.stderr.flush()
   if result:
      return result
   else:
      result = -1

limit=2
digitcount=50
score=0
count=0
ttanswer=4000


# Get the number with a time limit of 4 seconds
number = getnumber("/usr/share/asterisk/sounds/en_US_f_Allison/vm-enter-num-to-call",ttanswer,digitcount)


# Dial the number if it was successfully obtained
if number < 0 :
   sayit("/usr/share/asterisk/sounds/en_US_f_Allison/tt-somethingwrong")
   time_duration = 5
   time.sleep(time_duration) 
   print("SET VARIABLE EXTNEW " + str(number))
   sys.stdout.flush()
   sys.exit()
else:
   sayit("/usr/share/asterisk/sounds/en_US_f_Allison/vm-dialout")
   time_duration = 5
   time.sleep(time_duration)
   print("SET VARIABLE EXTNEW " + str(number))
   sys.stdout.flush()
   sys.exit()
sys.stdout.flush()
sys.exit()

Dieses Skript läuft nach meiner Erfahrung einwandfrei. Eine Endlosschleife konnte ich erfolgreich unterbinden. Es bricht bei fehlerhaften Eingaben ab. Allerdings wird ein Vollprofi in der AGI-Skript-Programmierung sicherlich einiges zu verbessern haben. Ich begrüße es ausdrücklich, wenn meine Skripte verbessert und erweitert werden.

Nachfolgend ein Auszug aus der extensions.conf, wie das Skript durch Wahl de Nummer 343 aufgerufen wird, um dann die eingetippte Ziffernabfolge zu wählen:

exten => 343,1,Answer()
  same => n,AGI(call.py)
  same => n,NoOP(from script call.py dialing ${EXTNEW})
  same => n,Goto(telefone,${EXTNEW},1)
  same => n,Hangup()

Obiges Beispiel ist nur ein Übungsbeispiel. Wollte man zum Beispiel das AGI-Skript nutzen, um ein Gateway von Iptel aufzubauen, könnte man folgende Erweiterung in der extensions.conf schreiben:

[iptel-in]
exten => 123456789,1,Goto(telefone,343,1)

Der Umweg über die Extension 343 ist nicht gerade elegant, aber einfach zu verstehen.

Und so sieht es in der Asterisk-Konsole aus, wenn jemand dieses Skript ausgeführt wird und dann die 2424 eingetippt wird:

Asterisk-Konsole: Die 1088 ruft mit der 343 das Python-AGI-Skript call.py auf und der Anrufer tippt die 2424 ein, die dann angerufen wird und den Anruf entgegennimmt.

Wer noch nie AGI-Skripte eingebunden hat, möge unbedingt die Tipps auf https://elektronikbasteln.pl7.de/schwe … -agi-php-skript .. um  nachlesen, um sich viel Zeit und Ärger zu ersparen. Entstanden ist dieses Skript durch die Anregungen von http://www.asteriskdocs.org … asterisk-CHP-9-SECT-4.htmlhttps://elektronikbasteln.pl7.de/mathe .. server und durch die im ersten Link beschriebenen Skripte für die schwedische und neuseeländische Wählscheibe.

Wer genau wissen will, wie das Skript funktioniert, kann das Skript in ChatGPT kopieren und nach einer Erklärung bitten.

CallThrough ohne AGI-Skript für Asterisk: Nachteil dieses Verfahrens ist, dass die Nummer abschließend mit der Raute abgeschickt werden muss.

Tastenfeld mit Rautetaste eines Telefons, das das Mehrfrequenzwahlverfahren beherrscht.
CallThrough-Funktion für Asterisk mit der Extensions.conf – 15. Februar 2023: Was ist CallThrough? Der Anrufer wählt zunächst eine Einwahlnummer. Per Sprachaufforderung wird er danach aufgefordert die gewünschte Zielnummer einzugeben. In diesem Beispiel wird die Zielnummer erst nach Eingabe des Raute-Symbols abgeschickt. Der Anrufer bekommt dann den Sprachhinweis, dass die gewünschte Verbindung aufgebaut wird. CallThrough kommt zum Beispiel zum Einsatz, wenn aus technischen Gründen eine Durchwahl nicht möglich ist. Wird dieses AGI-Skript für Python 2.7 oder Python 3 durch einen Telefonanruf aufgerufen, wird der Anrufer durch eine Sound-Datei aufgefordert seine gewünschte Zielnummer in seine Telefontastatur einzugeben. Das Skript wartet maximal vier Sekunden, bis die nächste Ziffer eingegeben wird. Wenn die 4 Sekunden ohne eine Aktion des Anrufers verstrichen sind, wird die bisher eingetippte Abfolge von Ziffern als Zahl an Asterisk übergeben, um damit in diesem Fall eine Nummer zu wählen. Es dient also dazu CallThrough zu ermöglichen. Dieser Artikel stellt auch eine Alternative für Callthrough ohne AGI-Skripte vor.  – weiter

Fazit: Zusammenfassend habe ich hier zwei Möglichkeiten dargestellt, wie sich CallThrough realisieren lässt. Einmal mit einem AGI-Script, bei dem der Anwender die zu wählende Nummer nicht mit einer Raute abschließen muss und als Alternative eine Lösung in der Extensions.conf, bei der der Anwender die gewählte Nummer hingegen mit der Raute abschließen muss.

Der Programmier-Code ist fehlerfrei. Trotzdem läuft das AGI-Skript nicht. Warum verflixt noch einmal?
AGI-Skripte für Asterisk unter Windows editieren – Was ist zu beachten? – 9. März 2023: Windows ist weit verbreitet. Deshalb schreiben nicht wenige ihre AGI-Skripte auf Windows, um sie dann in ihren Raspberry Pi zu kopieren. Wer einige Dinge nicht beachtet, wird die Skripte nicht zum Laufen bekommen. Hier sind die Fallstricke beschrieben, die mich viel Zeit gekostet haben. Die Beispiele beziehen sich auf Python. Sie haben für andere Programmiersprachen die entsprechende Gültigkeit. – weiter

Auf einem Raspberry Pi lässt sich ein kompletter Telefonserver betreiben, der seinen Teilnehmern über Landesgrenzen hinweg kostenloses Telefonieren von unterwegs oder daheim ermöglicht.
Asterisk-Telefonserver auf einem Raspberry Pi – Installation, Konfiguration, Programmierung, SIP, IAX2, AGI-Skripte, Sicherheit und Tipps zum praktischen Betrieb – 2.11.2022: Diese Seite richtet sich an jene, welche einen Asterisk-Telefon-Server auf einem Raspberry Pi betreiben möchten und später ein kleines Netzwerk aus Asterisk-Servern planen, um ein eigenständiges Telefonnetz aufzubauen. Los geht es mit der Installation von Raspbian und Asterisk auf einem Raspberry Pi und dann nach Lust und Laune immer tiefer in die Programmierung von Asterisk. Die Themen werden laufend erweitert.

Selbstverständlich muss es nicht unbedingt ein Raspberry Pi sein. Andere Linux-Rechner gehen auch. – weiter