18. Februar 2023
Mit diesem AGI-Skript in Python für Asterisk gelangt der Anrufende in ein Auswahlmenü. Nach dem Eingeben einer zweistelligen Nummer erhält er Zugang zu externen Telefonnetzen oder zu anderen Telefon-Servern. Nach dieser Vorauswahl kann er eine Zielnummer eintippen, um ein Endgerät im ausgewählten externen Telefonnetz oder Server zu erreichen.
Bedienung und Ablauf: Nach der Einwahl über eine bestimmte Einwahlnummer (im Beispiel zu Testzwecken 501) wird das Python-Skript aufgerufen. Es erfolgt eine Ansage, die erklärt welche der 12 Netze über welche zweistelligen Nummern erreichbar sind. Sobald der Nutzer diese Auswahl getroffen hat, wird die Ansage unterbrochen. Es erfolgt eine weitere Aufforderung die gewünschte Zielnummer im gewählten Netz einzutippen. Wenn 5 Sekunden lang keine weitere Eingabe über die Tastatur erfolgt, ertönt die Ansage, dass man nun weiterverbunden wird. Dann hört der Teilnehmer den Signalton und er wird verbunden. Gibt der Teilnehmer nach 5 Sekunden keine Ziffer ein, erfolgt eine Fehlermeldung und das Programm sorgt für das Auflegen des Telefons. Versäumt der Nutzer innerhalb von 5 Sekunden seine zweistellige Auswahlnummer einzugeben oder wählt eine ungültige Nummer, wird er in einer Endlosschleife erneut aufgefordert die Auswahl zu treffen.
Das Programm besteht im Kern aus einer if-Anweisung, die auf Grund der zweistelligen Nummern die Auswahl trifft. Die Auswahl erfolgt über die Wahl des Kontextes, denn für jedes externe Netz existiert ein isolierter Kontext in der extensions.conf. Diese Kontexte sind also nur über das Python-Programm erreichbar, um Konflikte mit anderen Wahlregeln zu vermeiden.
Wurde mit dieser if-Anweisung der richtige Kontext getroffen, muss nun die entsprechende Nummer gewählt werden, was wiederum mit einer weiteren if-Anweisung geschieht. In diesem Zusammenhang erfolgt die Aufforderung eine Nummer einzutippen. Ist die Nummer kleiner als Null, was programmintern passiert, wenn keine Nummer eingegeben wurde, erfolgt eine Fehlermeldung. Andernfalls wird die Nummer gewählt und es eine Stimme bestätigt den Verbindungsaufbau. Die zweite if-Anweisung befindet sich in einer Funktion, um das Skript kompakter zu halten.
Zudem sind noch weitere Funktionen am Anfang des Skriptes enthalten, die für die Kommunikation mit Asterisk benötigt werden. Sie sind auf
http://www.asteriskdocs.org/en/2nd_Edition … asterisk-CHP-9-SECT-4.html
beschrieben. Dadurch kommt das Programm ohne spezielle Asterisk-Module aus. Es benötigt also kein Pyst, Pyst2 oder Pyst3. Getestet ist das Programm auf Python 2.7. Es müsste aber auch auf Python 3 laufen. Einige Programmschnipsel sind mit Hilfe von ChatGPT erstellt worden, die dann nachgebessert werden mussten.
Das Python-Skript externeprovider.py: In welchem Ordner es unterzubringen ist, steht in der asterisk.conf. Diese befindet sich in dem Ordner, in dem sich auch die extensions.conf befindet.
#!/usr/bin/env python # -*- coding: utf-8 -*- # Name der Datei: externeprovider.py # 18:39 Freitag, 17. Februar 2023 # Auswahlmenue fuer externe Provider # Linux-Befehle zum Debugging und zum auf UNIX umwandeln: Erspart langes Suchen nach den Befehlen # sudo dos2unix /usr/share/asterisk/agi-bin/externeprovider.py # auf UNIX umstellen für Linux # sudo chmod 777 /usr/share/asterisk/agi-bin/externeprovider.py # wird ausführbar # sudo python /usr/share/asterisk/agi-bin/externeprovider.py # fürs Debugging ######### Nachfolgendes erspart den Einsatz von pyst ####################### # siehe Beschreibung auf # http://www.asteriskdocs.org/en/2nd_Edition/asterisk-book-html-chunk/asterisk-CHP-9-SECT-4.html import sys import re import time # Read and ignore AGI environment (read until blank line) 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 saynumber (params): sys.stderr.write("SAY NUMBER %s \"\"\n" % params) sys.stderr.flush() sys.stdout.write("SAY NUMBER %s \"\"\n" % 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 ################### Ab hier beginnt das eigentliche Skript ##################### # Eingabe der Nummer eines externen Providers ohne Vorwahl def number1(digitcountsnumber, ttanswersnumber): result1 = getnumber("/usr/share/asterisk/sounds/eigene/externprovider/waehlensie",ttanswersnumber,digitcountsnumber) if int(result1) < 0 : print("SET VARIABLE EXTNEW " + str(result1)) sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/externprovider/fehler") time_duration = 5 time.sleep(time_duration) else: print("SET VARIABLE EXTNEW " + str(result1)) sys.stdout.flush() # ohne flush hoert man das nachfolgende soundfile nicht sayit("/usr/share/asterisk/sounds/eigene/externprovider/verbindungsaufbau") time_duration = 2 time.sleep(time_duration) limit=5 # nicht definiert digitcount=2 # Der Nutzer kann nur max. 2 Ziffern eingeben und danach beginnt die Vermittlung ttanswer=5000 # Wenn nach 5000 ms nichts mehr eingeben wird, ist die Zeit überschritten digitcountsnumber = 50 # Maximal 50 Ziffern sind erlaubt bei Eingabe der Nummer ttanswersnumber = 5000 # Wenn 5000 ms nichts mehr eingeben wird, ist die Zeit überschritten time_duration = 1 # Ein Sekunde Pause eingebaut, damit der Nutzer den Anfang time.sleep(time_duration) # der Ansage mitbekommt. # Nachfolgend erfolgt das Abspielen eines WAV- oder GSM-Soundfiles, dass den Nutzer # informiert welche Ziffer er zu wählen hat. Der Nutzer kann während des Abspielens # der Sounddatei die gewünschte Ziffer wählen. Sogleich wird er weiterverbunden. # Die Sounddateien lassen sich z.B. mit Text to Speech auf https://ttsmp3.com/ als MP3 erstellen # und anschließend auf https://g711.org/ mit der Standardeinstellung in das g.711-Format # als Wav-Datei umwandeln, die für Asterisk geeignet ist. number = getnumber("/usr/share/asterisk/sounds/eigene/externprovider/halloliste",ttanswer,digitcount) # number ist die Ziffer oder die Zahl, die vom Nutzer in die Tastatur des Telefons eingetippt # wurde. # In der if-elif-else Anweisung wird sie in eine Ganzzahl umgewandelt. Diese If-Aweisung # bestimmt, welche Nummern (Extensions) gewählt werden, wenn man # eine Ziffer oder Zahl eintippt. # Tippt man die z.B. die 01 ein, dann wird mit Print ein String ausgedruckt, # der die Variable CONTEXTNEW setzt. Das ist der Context für die Extension # des externen Providers if int(number) == 1: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_cnet") elif int(number) == 2: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_callcentric") elif int(number) == 3: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_iptel") elif int(number) == 4: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_sip2sip") elif int(number) == 5: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_antisip") elif int(number) == 6: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dusnet") elif int(number) == 7: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_opensips") elif int(number) == 8: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_voipfone") elif int(number) == 9: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_sipcall") elif int(number) == 10: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_ippi") elif int(number) == 11: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dl1hrc") elif int(number) == 12: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dl5pd") else: sayit("/usr/share/asterisk/sounds/en_US_f_Allison/beep") time_duration = 2 time.sleep(time_duration) # Wenn der Nutzer die 5 Sekunden verstreichen lässt oder eine # nicht definierte Zahl eintippt, springt das Skript zu # else, es wird eine Sounddatei abgespielt die Piep macht # und dann geht es wieder von vorne los. # # exampel in extensions.conf # # [telefone] # exten => 501,1,Answer() # Hier im Demo die 501 wählen, um ins Menue zu kommen # same => n,AGI(externeprovider.py) # eventuell absolute Pfadangabe notwendig # same => n,NoOP(from script externeprovider.py dialing ${CONTEXTNEW} , ${EXTNEW}) # same => n,Goto(${CONTEXTNEW},${EXTNEW},1) # Hier geht es zum Context und zur Extension # same => n,Hangup() # ; Fuer jeden externen Provider gibt es einen eigenen isolierten Context, # , um Konflikte mit Wahlregeln zu vermeiden. Drei Beispiele: # # [goto_iptel] # exten => _X.,1,Dial(SIP/${EXTEN}@iptel,60,tT) # [goto_sip2sip] # exten => _X.,1,Dial(SIP/${EXTEN}@sip2sip,60,tT) # [goto_cnet] # exten => _X.,1,Dial(IAX2/outgoing-cnet/${EXTEN})
Aufruf des Skripts in der extensions.conf: Der Aufruf erfolgt im Beispiel durch die Wahl der Nummer 501. Das Skript wird ausgeführt, es werden zwei Parameter, jeweils einer für den Kontext (CONTEXTNEW) und einer für die gewählte Zielnummer (EXTNEW).
[telefone] exten => 501,1,Answer() # Hier im Demo die 501 wählen, um ins Menue zu kommen same => n,AGI(externeprovider.py) # eventuell absolute Pfadangabe notwendig same => n,NoOP(from script externeprovider.py dialing ${EXTNEW}) and ${CONTEXTNEW} same => n,Goto(${CONTEXTNEW},${EXTNEW},1) # Hier geht es zum Context und zur Extension same => n,Hangup()
Für jedes externe Netz stehen die Wahlregeln in einem isolierten Kontext, die ebenfalls sich in der extensions.conf befinden.
[goto_cnet] ; C*Net 01 exten => _X.,1,Dial(IAX2/outgoing-cnet/${EXTEN}) same => n,Gosub(hinweise,s,1) ; Gosub nicht notwendig, erzeugt nur Hinweise, wenn Besetzt, falsche Nummer u.s.w. [goto_callcentric] ; Callcentric 02 exten => _X.,1,Dial(SIP/${EXTEN}@callcentric,60,tT) same => n,Gosub(hinweise,s,1) [goto_iptel] ; Iptel 03 exten => _X.,1,Dial(SIP/${EXTEN}@iptel,60,tT) same => n,Gosub(hinweise,s,1) [goto_sip2sip] ; Sip2Sip 04 exten => _X.,1,Dial(SIP/${EXTEN}@sip2sip,60,tT) same => n,Gosub(hinweise,s,1) [goto_antisip] ; AntiSip 05 exten => _X.,1,Dial(SIP/${EXTEN}@antisip,60,tT) same => n,Gosub(hinweise,s,1) [goto_dusnet] ; Dusnet 06 exten => _X.,1,Dial(SIP/${EXTEN}@dusnet,60,tT) same => n,Gosub(hinweise,s,1) [goto_opensips] ; Opensips 07 exten => _X.,1,Dial(SIP/${EXTEN}@opensips,60,tT) same => n,Gosub(hinweise,s,1) [goto_voipfone] ; Voipfone 08 exten => _X.,1,Dial(SIP/${EXTEN}@voipfone,60,tT) same => n,Gosub(hinweise,s,1) [goto_sipcall] ; SipCall Österreich 09 exten => _X.,1,Dial(SIP/${EXTEN}@sipcall,60,tT) same => n,Gosub(hinweise,s,1) [goto_ippi] ; Ippi 10 exten => _X.,1,Dial(SIP/${EXTEN}@sipcall,ippi,60,tT) same => n,Gosub(hinweise,s,1) [goto_dl1hrc] ; DL1HRC 11 exten => _X.,1,Dial(IAX2/outgoing-dl1hrc/${EXTEN}) same => n,Gosub(hinweise,s,1) [goto_dl5pd] ; DL5PD 12 exten => _X.,1,Dial(IAX2/outgoing-rainer/${EXTEN}) same => n,Gosub(hinweise,s,1);
Dieser Aufwand war notwendig, damit die Wahlregeln nicht mit anderen Wahlregeln in Konflikt geraten können, denn die fremden Netze sind als Alternative noch über vierstellige Vorwahlen erreichbar, die auch mit Wählscheibentelefonen die Auswahl ermöglichen. Die Zeilen mit Gosub können entfallen. Sie liefern nur Hinweise, falls der Verbindungsaufbau scheitert.
Um als Beispiel die Wahlregeln für die Wahl in externe Netze zu verstehen, betrachten wir dies für die Einwahl in Iptel. Tippt jemand 03, sorgt das AGI-Skript dafür das die Variable CONTEXTNEW den String goto_iptel zurückgibt. Tippt jemand nach dieser Auswahl eine Nummer ein, dann wird diese an EXTNEW übergeben und dann wiederum an EXTEN.
[goto_iptel] ; Iptel 03 exten => _X.,1,Dial(SIP/${EXTEN}@iptel,60,tT)
Diese Wahlregel muss also “@iptel” finden. Diese Anweisung findet ihre Entsprechung in der sip.conf:
[general] ; sip.conf auszugsweise ; ; ; Die Registrierung ermöglicht Anrufe an Iptel entgegen zu nehmen register => 1234567:meinpasswort@iptel.org/1234567 [iptel] ; Hier steht auch iptel context=iptel-in host=iptel.org username=1234567 secret=meinpasswort fromuser=1234567 fromdomain=iptel.org canreinvite=no qualify=yes nat=force_rport,comedia type=peer port=5060
Nachfolgende Erklärungen sind nicht für das Verständnis des Skripts notwendig: Das Skript behandelt nur das Herauswählen in die externen Netze. Nachfolgend geht es um das Hineinwählen in den Asterisk-Server von fremden Netzen, um das gesamte Prinzip bei dieser Gelegenheit zu verstehen.
Zur Vollständigkeit deshalb hier noch die Erklärung, was passiert, wenn jemand meinen Asterisk-Server über Iptel anruft durch die Iptel-Nummer 1234567:
[iptel-in] ; im Iptel-Netz die Nummer 1234567 wählen exten => 1234567,1,Goto(callthrough,s,1) es geht zum Context callthrough mit der Einwahlroutine
Das [iptel-in] finden wir als Entsprechung in der sip.conf in [iptel]: Wir finden es unter [iptel] in der Zeile context=iptel-in.
; Auszug aus der Sip.conf [iptel] ; Hier steht auch iptel context=iptel-in ; hier steht iptel-in host=iptel.org username=1234567 secret=meinpasswort fromuser=1234567 fromdomain=iptel.org canreinvite=no qualify=yes nat=force_rport,comedia type=peer port=5060
Und jetzt noch die alternative Wahlregel für das Hineinwählen in das Iptel-Netz über eine Vorwahl:
; wir sind in der extensions.conf [telefone] ; hier stehen natürlich viele Wahlregeln und nicht nur eine ; Weiterleitung auf Iptel-Nummern (interne Vorwahl 8844) exten => _8844X.,1,Dial(SIP/${EXTEN:4}@iptel,60,tT) ;
Wählt man als Vorwahl die 8844 und dann als Durchwahl sofort die gewünschte Nummer im Iptel-Netz gelangt man zu dieser Nummer. Das Patternmatching “_8844X.” beinhaltet alle gewählten Nummern, die mit 8844 beginnen. Die Anweisung “${EXTEN:4}” sorgt dafür, dass von der gewählten Nummer die ersten vier Ziffern abzuschneiden sind. Wählt jemand zum Beispiel 884478901, wird die Iptel-Nummer 78901 angewählt.
Und nun das Ganze noch an Hand einer IAX2-Verbindung erklärt: Mein Server ist zum Beispiel mit dem von Rainer, DL5PD über IAX2 erreichbar, wenn im AGI-Skript die 12 gewählt wird. Dann gelangt man in den folgenden Kontext:
[goto_dl5pd] ; DL5PD 12 exten => _X.,1,Dial(IAX2/outgoing-rainer/${EXTEN}) ; outgoing-rainer ist entscheidend same => n,Gosub(hinweise,s,1)
Im Prinzip fast wie bei SIP. Nachfolgend die Entsprechung in der iax.conf:
; wir sind in der iax.conf [outgoing-rainer] ; hier steht auch wieder das "outgoing-rainer" type=peer ; peer bedeutet nur ausgehend username=incoming-volker host=blabal.blall.de secret=blablabla context=telefone trunk=yes qualify=yes requirecalltoken=no calltokenoptional=yes authdebug=yes
Und so sieht das alternative Anwählen von Rainers Server mit Vorwahl aus:
; wir sind in der extensions.conf (nur Auszug dargestell) [telefone] ; IAX2-Verbindung zu Rainer exten => _8840X.,1,NoOp(IAX2 - Volker ruft Rainer an mit Vorwahl 8840) same => n,Dial(IAX2/outgoing-rainer/${EXTEN:4}) ; hier steht das entscheidende outgoing-rainer same => n,Gosub(hinweise3,s,1) ; kann man sich sparen, will nur eigene Warnmeldungen hören. ; https://das-asterisk-buch.de/1.6/applications-gotoif.html ; https://www.voip-info.org/asterisk-variable-dialstatus/
Die Sound-Dateien, welche das AGI-Skript aufruft:
Download aller Audiodateien als mp3 und wav: Audiodateien fuer externeprovider.py.zip
halloliste.wav:
waehlensie.wav:
verbindungsaufbau.wav:
fehler.wav:
Erzeugt wurden die Soundfiles mit https://ttsmp3.com/ und der deutschen Sprecherin Marlene. Die mp3-Dateien wurden dann auf https://g711.org/ mit der Voreinstellung (Standard Definition 16-bit WAV (8 kHz, Mono, 16-Bit PCM) in WAV-Dateien umgewandelt, die Asterisk verarbeiten kann. Die WAV-Dateien klingen etwas dumpfer als die MP3-Dateien.
Für die Spracherzeugung mit TTSMP3 habe ich mir den Text für die halloliste.wav abgespeichert:
Für TTSMP3 https://ttsmp3.com/ und deutsche Sprecherin Marlene: Hallo und herzlich Willkommen! Bitte wählen Sie folgende Auswahlnummern, um in die externen Telefonnetze zu gelangen:<break time="1s"/> Si Star Net: Null Eins,<break time="1s"/> Kolzentrik: Null Zwei,<break time="1s"/> Iptel: Null Drei,<break time="1s"/> SippTuSipp: Null Vier,<break time="1s"/> AntiSipp: Null Fünf,<break time="1s"/> Dus Punkt Net: Null Sechs,<break time="1s"/> Opensipps: Null Sieben,<break time="1s"/> Voipfon: Null Acht,<break time="1s"/> SipKohl Österreich: Null Neun,<break time="1s"/> Ippi: Zehn,<break time="1s"/> D L Eins H R C: Elf,<break time="1s"/> D L Fünf P D: Zwölf,<break time="1s"/>
Dadurch sind Anpassungen bei der Änderung der Liste schnell erstellt. Man beachte die merkwürdige Schreibweise, damit die Aussprache der englischen Wörter halbwegs stimmt.
********************
20. Februar 2023
Nachfolgend die Version 2 des Auswahlmenüs:
Was hat sich geändert? Es sind mehr Provider für die Auswahl hinzugekommen. Es gibt jetzt noch eine Ansage mit weiteren Informationen unter 88 und nach dem Eintippen der Nummern erfolgt ein Aufsagen der gewählten Nummer Ziffer für Ziffer für die Kontrolle. Dafür wurde die Definition “saydigits” geschaffen. Dann erfolgt de Verbindungsaufbau. Die Ansagen wurden überarbeitet.
Abgesehen von den notwendigen Definitionen für die Kommunikation mit Asterisk kommen drei Definitionen (def) zum Einsatz:
def getnumber: Holt sich die zweistelligen Auswahlnummern, die in die Tastatur als DTMF eingeben wurden.
def number1: Holt sich die eingetippten Telefonnummern, die in die Tastatur getippt wurden.
def saydigits: Sagt die eingetippte Nummer als Abfolge von Ziffern auf, bevor sie versendet wird.
Die def number1 ruft die beiden Definitionen def getnumber und die def saydigits für eine if-Anweisung auf. Die def number1 wird wiederum in einer weiteren einer if-schleife verwendet.
#!/usr/bin/env python # -*- coding: utf-8 -*- # Name der Datei: externeprovider.py # 17:57 Montag, 20. Februar 2023 Version 2 ######### Nachfolgendes erspart den Einsatz von pyst ####################### import sys import re import time # Read and ignore AGI environment (read until blank line) 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 saynumber (params): sys.stderr.write("SAY NUMBER %s \"\"\n" % params) sys.stderr.flush() sys.stdout.write("SAY NUMBER %s \"\"\n" % 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 ################### Ab hier beginnt das eigentliche Skript ##################### def saydigits (params): sys.stderr.write("SAY DIGITS %s \"\"\n" % params) sys.stderr.flush() sys.stdout.write("SAY DIGITS %s \"\"\n" % params) sys.stdout.flush() result = sys.stdin.readline().strip() checkresult(result) # Eingabe der Nummer eines externen Providers ohne Vorwahl def number1(digitcountsnumber, ttanswersnumber): result1 = getnumber("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_waehlensie",ttanswersnumber,digitcountsnumber) if int(result1) < 0 : print("SET VARIABLE EXTNEW " + "5553") sys.stdout.flush() print("SET VARIABLE CONTEXTNEW " + "telefone") sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_fehler") time_duration = 4 time.sleep(time_duration) else: print("SET VARIABLE EXTNEW " + str(result1)) sys.stdout.flush() # ohne flush hoert man das nachfolgende soundfile nicht sayit("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_nummer") time_duration = 3 # neu time.sleep(time_duration) saydigits(result1) sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_verbindungsaufbau") time_duration = 2 time.sleep(time_duration) limit=5 # nicht definiert digitcount=2 # Der Nutzer kann nur max. 2 Ziffern eingeben und danach beginnt die Vermittlung ttanswer=5000 # Wenn nach 5000 ms nichts mehr eingeben wird, ist die Zeit überschritten digitcountsnumber = 50 # Maximal 50 Ziffern sind erlaubt bei Eingabe der Nummer ttanswersnumber = 8000 # Wenn 8000 ms nichts mehr eingeben wird, ist die Zeit überschritten time_duration = 1 # Ein Sekunde Pause eingebaut, damit der Nutzer den Anfang time.sleep(time_duration) # der Ansage mitbekommt. # Nachfolgend erfolgt das Abspielen eines WAV- oder GSM-Soundfiles, dass den Nutzer # informiert welche Ziffer er zu wählen hat. Der Nutzer kann während des Abspielens # der Sounddatei die gewünschte Ziffer wählen. Sogleich wird er weiterverbunden. # Die Sounddateien lassen sich z.B. mit Text to Speech auf https://ttsmp3.com/ als MP3 erstellen # und anschließend auf https://g711.org/ mit der Standardeinstellung in das g.711-Format # als Wav-Datei umwandeln, die für Asterisk geeignet ist. number = getnumber("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_halloliste",ttanswer,digitcount) # number ist die Ziffer oder die Zahl, die vom Nutzer in die Tastatur des Telefons eingetippt # wurde. # In der if-elif-else Anweisung wird sie in eine Ganzzahl umgewandelt. Diese If-Aweisung # bestimmt, welche Nummern (Extensions) gewählt werden, wenn man # eine Ziffer oder Zahl eintippt. # Tippt man die z.B. die 01 ein, dann wird mit Print ein String ausgedruckt, # der die Variable CONTEXTNEW setzt. Das ist der Context für die Extension # des externen Providers if int(number) == 1: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_cnet") elif int(number) == 2: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_callcentric") elif int(number) == 3: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_iptel") elif int(number) == 4: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_sip2sip") elif int(number) == 5: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_antisip") elif int(number) == 6: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dusnet") elif int(number) == 7: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_opensips") elif int(number) == 8: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_voipfone") elif int(number) == 9: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_sipcall") elif int(number) == 10: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_ippi") elif int(number) == 11: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dl1hrc") elif int(number) == 12: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_dl5pd") elif int(number) == 13: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_sipbroker") elif int(number) == 14: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_volkerstestserver") elif int(number) == 15: number1(digitcountsnumber,ttanswersnumber) print("SET VARIABLE CONTEXTNEW " + "goto_zadarma") elif int(number) == 88: print("SET VARIABLE EXTNEW " + "5553") # Diese Menue wird erneut angerufen und wiederholt sich sys.stdout.flush() print("SET VARIABLE CONTEXTNEW " + "telefone") sys.stdout.flush() sayit("/usr/share/asterisk/sounds/eigene/auswahlmenues/externeprovider_informationen") time_duration = 44 time.sleep(time_duration) elif int(number) == 0: # Zurück zum Hautptmenue 5550 bei Wahl der Null print("SET VARIABLE EXTNEW " + "5550") sys.stdout.flush() print("SET VARIABLE CONTEXTNEW " + "telefone") sys.stdout.flush() else: sayit("/usr/share/asterisk/sounds/en_US_f_Allison/beep") time_duration = 2 time.sleep(time_duration) # Wenn der Nutzer die 5 Sekunden verstreichen lässt oder eine # nicht definierte Zahl eintippt, springt das Skript zu # else, es wird eine Sounddatei abgespielt die Piep macht # und dann geht es wieder von vorne los. # # exampel in extensions.conf # # [telefone] # exten => 501,1,Answer() # Hier im Demo die 501 wählen, um ins Menue zu kommen # same => n,AGI(externeprovider.py) # eventuell absolute Pfadangabe notwendig # same => n,NoOP(from script externeprovider.py dialing ${CONTEXTNEW} , ${EXTNEW}) # same => n,Goto(${CONTEXTNEW},${EXTNEW},1) # Hier geht es zum Context und zur Extension # same => n,Hangup() # ; Fuer jeden externen Provider gibt es einen eigenen isolierten Context, # , um Konflikte mit Wahlregeln zu vermeiden. Drei Beispiele: # # [goto_iptel] # exten => _X.,1,Dial(SIP/${EXTEN}@iptel,60,tT) # [goto_sip2sip] # exten => _X.,1,Dial(SIP/${EXTEN}@sip2sip,60,tT) # [goto_cnet] # exten => _X.,1,Dial(IAX2/outgoing-cnet/${EXTEN})
Nachfolgend der Auszug aus der extensions.conf:
[goto_cnet] ; C*Net 01 exten => _X.,1,Dial(IAX2/outgoing-cnet/${EXTEN}) [goto_callcentric] ; Callcentric 02 exten => _X.,1,Dial(SIP/${EXTEN}@callcentric,60,tT) [goto_iptel] ; Iptel 03 exten => _X.,1,Dial(SIP/${EXTEN}@iptel,60,tT) [goto_sip2sip] ; Sip2Sip 04 exten => _X.,1,Dial(SIP/${EXTEN}@sip2sip,60,tT) same => n,Gosub(hinweise,s,1) [goto_antisip] ; AntiSip 05 exten => _X.,1,Dial(SIP/${EXTEN}@antisip,60,tT) same => n,Gosub(hinweise,s,1) [goto_dusnet] ; Dusnet 06 exten => _X.,1,Dial(SIP/${EXTEN}@dusnet,60,tT) same => n,Gosub(hinweise,s,1) [goto_opensips] ; Opensips 07 exten => _X.,1,Dial(SIP/${EXTEN}@opensips,60,tT) same => n,Gosub(hinweise,s,1) [goto_voipfone] ; Voipfone 08 exten => _X.,1,Dial(SIP/${EXTEN}@voipfone,60,tT) same => n,Gosub(hinweise,s,1) [goto_sipcall] ; SipCall Österreich 09 exten => _X.,1,Dial(SIP/${EXTEN}@sipcall,60,tT) same => n,Gosub(hinweise,s,1) [goto_ippi] ; Ippi 10 exten => _X.,1,Dial(SIP/${EXTEN}@ippi,60,tT) same => n,Gosub(hinweise,s,1) [goto_dl1hrc] ; DL1HRC 11 exten => _X.,1,Dial(IAX2/outgoing-dl1hrc/${EXTEN}) same => n,Gosub(hinweise,s,1) [goto_dl5pd] ; DL5PD 12 exten => _X.,1,Dial(IAX2/outgoing-rainer/${EXTEN}) same => n,Gosub(hinweise,s,1); [goto_sipbroker] ; Sipbroker 13 exten => _X.,1,Dial(SIP/${EXTEN}@sipbroker,60,tT) same => n,Gosub(hinweise,s,1) [goto_volkerstestserver] ; Volkers Testserver Notebook 14 exten => _X.,1,Dial(IAX2/outgoing-lubuntu/${EXTEN}) same => n,Gosub(hinweise,s,1); [goto_zadarma] ; Ippi 15 exten => _X.,1,Dial(SIP/${EXTEN}@zadarma,60,tT) same => n,Gosub(hinweise,s,1)
Die neuen Soundfiles als Zip: Audio-Dateien-Externe-Provider-Version-2
Die Ansage für die Auswahlliste:
Texte für die Spracherzeugung mit https://ttsmp3.com/:
Hallo! Hier erhalten Sie Zugänge zu externen Telefonnetzen und angeschlossenen Providern. Nach der Eingabe der zweistelligen Zugangsnummer werden Sie aufgefordert eine Telefonnummer des externen Netzes einzugeben. <break time="1s"/> Sie Star Net:<prosody rate="slow"> 0 1 </prosody><break time="1s"/> Callcentric:<prosody rate="slow"> 0 2 </prosody><break time="1s"/> Iptel:<prosody rate="slow"> 0 3 </prosody><break time="1s"/> Sip tu Sip:<prosody rate="slow"> 0 4 </prosody><break time="1s"/> Anti Sip:<prosody rate="slow"> 0 5 </prosody><break time="1s"/> Dus Punkt Net:<prosody rate="slow"> 0 6 </prosody><break time="1s"/> Open Sips:<prosody rate="slow"> 0 7 </prosody><break time="1s"/> Voip Phone:<prosody rate="slow"> 0 8 </prosody><break time="1s"/> Sip Call Österreich:<prosody rate="slow"> 0 9 </prosody><break time="1s"/> Ippi:<prosody rate="slow"> 10 </prosody><break time="1s"/> D L Eins H R C:<prosody rate="slow"> 11 </prosody><break time="1s"/> D L Fünf P D:<prosody rate="slow"> 12 </prosody><break time="1s"/> Sip Broker:<prosody rate="slow"> 13 </prosody><break time="1s"/> Volkers Test Server:<prosody rate="slow"> 14 </prosody><break time="1s"/> Zadarma:<prosody rate="slow">: 15 </prosody><break time="1s"/> Informationen über den Zugang zu den externen Netzen:<prosody rate="slow"> 88 </prosody><break time="1s"/> Zurück zum Hauptmenü:<prosody rate="slow"> 0 0 </prosody><break time="1s"/> Die Telefonnummer können Sie übrigens mit der Raute-Taste abschließen, um den Wählvorgang zu beschleunigen. Andernfalls verzögert sich der Verbindungsaufbau um mehrere Sekunden. Die zweistelligen Auswahl-Nummern können Sie während der laufenden Ansage eingeben. Es erfolgt die Wiederholung der Ansage. ................................................................ externeprovider_informationen.wav Die Möglichkeit sich mit externen Telefonnetzen verbinden zu lassen ist ein zentraler Bestandteil dieses Telefon-Servers. Diese Funktion ermöglicht Ihnen kostenlose Telefonate in den wichtigsten geschlossenen Telefonnetzen, ohne dass Sie sich dafür anmelden müssen. Wenn Sie wie üblich eine Flatrate für das deutsche Festnetz besitzen, können Sie zum Beispiel jede Telefonnummer von Iptel erreichen, die sich jeder kostenlos beschaffen kann. Umgekehrt ist Volkers Server auch über die Iptel-Nummer 88 0 0 73 erreichbar. Wenn Sie eine eigene Telefonnummer von Volkers Asterisk-Server besitzen, sind Sie nicht nur über Iptel erreichbar sondern über viele andere Telefonnetze.
Mit den abgespeicherten Texten lassen sich Änderungen schneller vornehmen.
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 – |
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 – |