Verschicken von Textnachrichten über das SIP-Protokoll mit Asterisk

3.11.2022

Ab Asterisk 11 lassen sich  Textnachrichten über das SIP-Protokoll verschicken. Vorrausetzung ist, dass die Endgeräte diese Funktion ebenfalls unterstützen. Die meisten Softphones sind dazu in der Lage. Für den Empfang der Texte müssen die empfangenen Endgeräte zudem eingeschaltet sein. Eine Zwischenspeicherung findet leider nicht statt.

Die Textübertragung mit SIP geht laut den Anregungen von Conrad de Wet unter https://github.com/InnovateAsterisk/Browser-Phone und nach einer Anleitung von Gerd (einen ganz großen Dank an dieser Stelle) ganz einfach. Getestet auf Raspberry Pi 3 B+, Raspbian vom Februar 2020, und Asterisk 16.2.1. Die Textübermittlungen wurden mit den Softphones PhonerLite,  Sipnetic, Linphone für Android und MicroSip getestet.

Damit man Textnachrichten über dafür geeignete Softphones verschicken und empfangen kann, müssen die sip.conf und extensions.conf um ein paar Einträge ergänzt werden. Das ist alles.

Egänzungen in der sip.conf:

[general] ; Wir sind in der sip.conf

; Am Ende von [general] sind folgende drei Zeilen
; für die Textübermittlung einzutragen.

accept_outofcall_message=yes ; muss unbedingt auf yes stehen
auth_message_requests=no
outofcall_message_context=textmessages

Ergänzungen in der extensions.conf:

; Wir sind in der extensions.conf

[textmessages] 
; Nachfolgend werden alle Telefonanschlüsse aufgeführt,
; die schreiben und lesen dürfen. Diese extensions (Nummern) sind
; bereits in den entsprechenden Contexten und in der sip.conf
; definiert.

exten => 1001,1,Gosub(send-text,s,1,(1001))
exten => 1002,1,Gosub(send-text,s,1,(1002))
exten => 1003,1,Gosub(send-text,s,1,(1003))

; die Nummern 1001, 1002 und 1003 dürfen also
; in diesem Beispiel
; Texte verschicken und empfangen. Man kann das
; natürlich eleganter schreiben. Kommt später.

; Und das muss auch noch reinkopiert werden:
[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,Set(FROM=${SHELL(asterisk -rx 'sip show endpoint ${PEER}' | grep 'callerid ' | cut -d':' -f2- | sed 's/^\ *//' | tr -d '\n')})
exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),<sip:${PEER}@,<sip:${CALLERID_NUM}@)})
exten => s,n,MessageSend(sip:${ARG1},${FROM_SIP})
exten => s,n,Hangup()

Mir was es zu anstrengend für die Nummern 1000 bis 1999 Nummer für Nummer 1000 Zeilen zu schreiben, weshalb ich [textmessages] umgeschrieben habe:

[textmessages]
exten => _1XXX,1,Gosub(send-text,s,1(${EXTEN}))
; mit einer Zeile sind die Nummern 1000 bis 1999 abgedeckt.

Nach den Änderungen an der sip.conf und an der extensions.conf habe ich in der Asterisk-Konsole den Befehl “core reload” ausgeführt. Danach wurden Änderungen an der extensions.conf mit “dialplan reload” akzeptiert.

Versenden von Texten mit dem Windows-Softphone PhonerLite.
Beim Softphone PhonerLite lässt sich die Textnachrichten-Funktion über Menüleiste – Optionen – Nachrichten aktivieren.

Vom Softphone Sipnetic ging das Verschicken von Textnachrichten an PhonerLite manchmal nur, wenn ich die Zielnummer neu eingegeben hatte. Ich konnte nicht direkt antworten. Es gelang mir eine Nachricht an mein IP-Telefon Thomson TB-30 zu verschicken. Der empfangene Text blinkte dann auf dem  Display. Er verschwand erst nach dem Unterbrechen der Stromversorgung.

Leider speichert Asterisk die Texte nicht zwischen. Texte kommen nur an, wenn das Softphone der Zielnummer eingeschaltet ist. Ein Telefongespräch muss nicht laufen. Ob die Textnachricht tatsächlich angekommen ist, erfährt man nicht. Man erfährt nur, ob die Textnachricht erfolgreich abgeschickt worden ist.

Selbst das IP-Telefon Thomson TB-30 kann Textnachrichten empfangen. Nachteil: Die Meldung verschwindet erst, wenn man die Stromversorgung unterbricht oder die Gegenstelle ein Leerzeichen sendet. Ist das TB-30 an einer FritzBox angemeldet, funktioniert die Textübermittlung nicht.

Nachtrag vom 21.9.2021: Leider hatte mit dem obigen Skript das Chatten auf dem Android-Smartphone Sipnetic nicht funktioniert. Wollte man auf einen Text direkt antworten, kam eine Fehlermeldung, weil die SIP-URI (die Sip-Adresse) der eingehenden Textnachricht falsch übermittelt wurde.

Das Skript in der extensions.conf habe ich deshalb angepasst und es läuft bei mir einwandfrei auf Asterisk 16.2.1:

; Auszug aus der extensions.conf 
; Skript für das Versenden von Texten über das SIP-Protokoll
; getestet auf Asterisk 16.2.1

[textmessages]
exten => _1XXX,1,Gosub(send-text,s,1,(${EXTEN}))
;
[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,NoOp(Message from: ${MESSAGE(from)})
exten => s,n,NoOp(Message to: ${MESSAGE(to)})
exten => s,n,NoOp(Message body: ${MESSAGE(body)})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,NoOp(peer: ${PEER})
exten => s,n,MessageSend(sip:${ARG1},${PEER})
exten => s,n,Hangup()

; ohne die NoOp-Anweisung mitgezählt besteht [send-text] aus drei Zeilen.

Ich habe noch nicht ganz verstanden, warum es funktioniert. In der sip.conf habe ich die einzelnen Telefonnummern der Nebenstellen recht schlicht gehalten und immer CallerIDs versehen. Hier ein Muster-Beispiel:

; Wir sind in der sip.conf
; Jede Ähnlichkeit mit der Wirklichkeit ist rein zufällig.
; Musteraufbau eines Accounts mit der Nummer 1444

[1444] ; Magdalena Andersson
type=friend
context=statsminister
secret=skatteparadisetsverige
host=dynamic
username=1444
canreinvite=no
port=5064
dtmfmode=rfc2833
callerid = "Magdalena Andersson" <1444>

In Sipnetic funktioniert jetzt das Chatten einwandfrei:

Chatten in Sipnetic geht jetzt. Zum Einsatz kann auch die Spracherkennung kommen, was das Tippen erspart.

Im Asterisk-CLI mit Verbose 3 ist folgendes zu sehen:

Die sieht man beim Chatten im CLI von Asterisk. Zum Vergrößern auf das Bild klicken.

Aus Sicherheitsgründen sind die IP-Adressen rot übermalt. Zu sehen ist eine Text-Konversation zwischen den Nummern 1003 und 1009. Die 1003 ist ein Sipnetic-Sofphone auf Android und die 1009 ist PhonerLite unter Windows.

Verbesserte Version mit Fehlermeldung bei Empfangsstörungen: Störend ist, dass keine Fehlermeldung erfolgt, wenn der Empfänger der Textnachricht offline ist, weil er zum Beispiel seinen PC ausgeschaltet hat oder das Softphone abgeschaltet ist. Das nachfolgende verbesserte Script schickt eine Fehlermeldung zurück, falls die Nachricht nicht angekommen ist.

[textmessages]
exten => _1XXX,1,Gosub(send-text,s,1,(${EXTEN}))  ; in GOSUB ist die erste Variable EXTEN und entspricht
                                                  ; in [send-text] ARG1
                                                  ; EXTEN enthält Empfänger-Nummer, z.B 1003

; Zum Versenden brauchen wir die Ziel-Telefonummer, im Beispiel die 1003. Die haben wir schon.
; Dann brauchen wir noch die Absender-Nummer, z.B. die 1009. Diese Nummer zu erhalten ist das eigentliche Problem.
; Nach dem Versenden der Nachricht, stellen wir fest, ob sie erfolgreich war. Wenn nicht lassen wir
; eine Fehlermeldung in umgekehrter Richtung verschicken.

[send-text]
                                                  ; Das sollte man wissen:
                                                  ; NoOP hat keine Funktion, dient nur zur Info für Fehlersuche
exten => s,1,NoOp(Sending Text To: ${ARG1})       ; von [send-text] ARG1 enthält Empfänger-Telefon-Nummer, z.B 1003
exten => s,n,NoOp(Message from: ${MESSAGE(from)}) ; vollständige SIP-Adresse des Absenders. Da steht zu viel drin.
exten => s,n,NoOp(Message to: ${MESSAGE(to)})     ; vollständige SIP-Adresse des Empfängers, hier nur zur Info
exten => s,n,NoOp(Message body: ${MESSAGE(body)}) ; Inhalt der Textnachricht

                                                  ; Nachfolgend geht es jetzt richtig los mit dem Skript
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)}) ; schneidet die Absender-Telefon-Nummer heraus.
exten => s,n,NoOp(peer: ${PEER})                  ; in PEER steht jetzt nur die Absender-Telefonnummer, z.B. 1009
exten => s,n,MessageSend(sip:${ARG1},${PEER})     ; versendet Nachricht nach 1003 (ARG1) mit Absenderadresse 1009 (PEER)

                                                  ; Nachricht wurde abgeschickt. Nachfolgend die Fehlerbehandlung
exten => s,n,NoOp(Message send status: ${MESSAGE_SEND_STATUS})             ; zeigt den Status des Versendes an
exten => s,n,GotoIf($["${MESSAGE_SEND_STATUS}" = "SUCCESS"]?erfolg:failed) ; erfolgreich versendet, gehe zu Zeile erfolg, 
                                                                           ; andernfalls zur Zeile failed                       

exten => s,n(erfolg),Hangup()                    ; bei erfolgreicher Versendung die Verbindung trennen
exten => s,n(failed),Set(MESSAGE(body)=Übertragungsfehler zum Teilnehmer ${ARG1}: ${MESSAGE_SEND_STATUS}) 
                                                 ; obige Zeile: Verfasst den Nachrichtinhalt im Falle eines Fehlers 
      
exten => s,n,MessageSend(sip:${PEER},${ARG1})    ; schickt den neuen Nachrichteninhalt mit der Fehlermeldung
                                                 ; von 1009 nach 1003. ARG1 und PEER wurden vertauscht

exten => s,n,Hangup()                            ; Verbindung trennen. Das wars schon.

; Falls die Nachricht nicht ankommt, erscheint beim Absender eine Fehlermeldung.
; Richtet sich die Textnachricht an ein normales Telefon, das erreichbar ist,
; erscheint keine Fehlermeldung, obwohl es keine Texte empfangen kann.
Asterisk-Konsele: Erfolgreiches Versenden einer Textnachricht “Bitte rufe mich an, wenn du da bist.” von der Telefonnummer 1009 zur Telefonnummer 1003. Getestet mit Asterisk 16.2.1. Für eine Großdarstellung auf das Bild klicken.

Abgefragt wird der Status von MESSAGE_SEND_STATUS. Liefert diese Funktion nicht SUCCESS zurück, wird eine Fehlermeldung an den Absender geschickt.

Es erscheint eine Fehlermeldung, weil die Gegenstelle nicht angemeldet ist.

Wenn man jedoch versucht an ein ganz normales Telefon, das keine Texte empfangen kann, eine Nachricht zu schicken, erfolgt leider kein Fehlermeldung, falls dieses Telefon erreichbar  – d.h. angeschlossen – ist.

Schickt man versehentlich eine Textnachricht an ein altes Telefon, erfolgt leider keine Fehlermeldung, wenn es am SIP-Server über eine FritzBox angemeldet ist.

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