Kapitel 13. Warteschleifen für Call-Center

Warteschleifen sind der Horror vieler Kunden. Trotzdem sind sie in kaum einer Firma mit einem telefonischen Kunden-Service mehr wegzudenken, daher soll hier beschrieben werden, wie man eine Warteschleife anlegt und welche Einstellungen möglich sind.[116]

Hier sind für uns insbesondere 4 Dateien wichtig:

queues.conf
Definiert Warteschlangen.
agents.conf
Definiert „Agenten“, also die Mitarbeiter, die die Anrufe entgegennehmen.
musiconhold.conf
Definiert die Wartemusik.
extensions.conf
Im Dialplan werden Anrufe mit der Queue()in eine Schlange eingereiht und Agenten mit AgentLogin() oder AgentCallbackLogin() am System registriert.

Eine häufige Ursache für Missverständnisse ist die Verwechslung von „Queue Members“ mit Anrufern: Mit den Mitgliedern einer Warteschlange sind immer nur die Agenten, nicht die Anrufer gemeint.[117]

Agenten können mehreren Schlangen angehören, und wir wollen, dass sich jeder Agent von einer beliebigen Extension aus einloggen kann („Hot-desking“). Das grundsätzliche Vorgehen zur Definition von Agenten und Warteschlangen sieht vereinfacht so aus:

agents.conf

[agents]
;    AgentenNr,Passwort,Name
agent => 1001,1234,Mark Spencer
agent => 1002,1234,Will Meadows

queues.conf

[support-schlange]
member => Agent/1001   ; Agent 1001 zur support-schlange hinzufügen
member => Agent/1002   ;   ... 1002 ...

extensions.conf

exten => 20,1,Queue(support-schlange)   ; => Warteschlange
exten => 25,1,AgentLogin()              ; bei Anruf Login

Und so funktioniert's: Die Agenten loggen sich über Extension 25 ein, hören fröhliche Musik und warten auf Anrufer. Anrufe auf Extension 20 werden in die Schlange aufgenommen und der Reihe nach (FIFO) abgearbeitet: Der Agent hört einen Piepton, und der erste Anrufer wird verbunden.

1. Extension zum Musiktest

Wir legen uns in der extensions.conf folgende Extension an, mit der wir gleich die ordnungsgemäße Funktion der Wartemusik überprüfen können:

exten => 222,1,Answer()                  ; abheben
exten => 222,n,Set(MUSICCLASS()=default) ; Musikklasse "default" setzen
exten => 222,n,WaitMusicOnHold(20)       ; 20 Sekunden Musik
exten => 222,n,Hangup()                  ; auflegen

2. musiconhold.conf

Beginnen wir mit dem Einfachsten, der Festlegung der Wartemusik (Music on Hold).

Die Einstellmöglichkeiten werden in der Datei musiconhold.conf beschrieben, ein paar Infos zur MP3-Unterstützung durch das Programm mpg123 stehen in doc/README.mp3 (1.2) / doc/mp3.txt (1.4). Die optimale Abtastrate für die MP3s ist 8000 Hz, mono.

Die einfachste (Default-)Konfiguration mit nur einer Musikklasse (default):

[default]
mode=quietmp3
directory=/var/lib/asterisk/mohmp3

Jetzt stoppen und starten wir Asterisk, z.B. mit asterisk -rx "stop now" und asterisk, um die neue Extension und die Streams zu aktivieren (ein Reload reicht nicht aus). Wenn Sie jetzt die Extension 222 (wie oben definiert) anrufen, sollten Sie Wartemusik hören (im o.g. Verzeichnis werden bereits 3 Dateien mitgeliefert).

Besser ist aber folgende Konfiguration, für die wir ein Verzeichnis moh-native in /var/lib/asterisk/ anlegen:

[default]
mode=files   ; Dateien direkt in einem Asterisk-kompatiblen Format lesen
directory=/var/lib/asterisk/moh-native
random=yes   ; Dateien in zufälliger Reihenfolge spielen

Es ist dazu notwendig, die MP3-Dateien in ein Asterisk-kompatibles Format zu konvertieren, was z.B. mit den Programmen sox[118] und lame[119] so geht:

$  lame --decode musik.mp3 musik.wav
$  sox -V musik.wav -r 8000 -c 1 -w musik.raw
$  sox -V musik.wav -r 8000 -c 1 -w musik.gsm

Damit haben wir eine fiktive Datei musik.mp3 ins WAV- und dann in die Formate RAW und GSM konvertiert (mit einer Abtastrate von 8 kHz). Wir können in unserem Verzeichnis auch Formate wie aLaw und uLaw (Dateiendungen für sox: .al und .ul) ablegen - Asterisk wird automatisch das am wenigsten CPU-intensive wählen. Wichtig ist, dass die Abtastrate immer 8000 Hz beträgt.

[Wichtig]Wichtig

Wenn Sie in der modules.conf nicht autoload benutzen, müssen Sie sicherstellen, dass die Module für die Formate, die Sie hier verwenden wollen, vor res_musiconhold geladen werden.

In unserem Test haben wir die im Verzeichnis moh/ mitgelieferten WAV-Dateien in unser Verzeichnis moh-native/ kopiert, mit sox nach RAW und GSM umkodiert und dann die WAV-Dateien, die Probleme bereiteten, aus dem Verzeichnis gelöscht:

$  cd /var/lib/asterisk/moh-native/
$  for i in *.wav; do \
sox $i -r 8000 -c 1 $(basename $i .wav).raw; \
sox $i -r 8000 -c 1 $(basename $i .wav).gsm; \
done
$  rm *.wav

Für CPU-kritische Anwendungen (große Installationen) sollten Sie einen Blick auf die Beschreibung in contrib/utils/README.rawplayer im ursprünglichen Asterisk-Verzeichnis werfen.

Weitere Musikklassen lassen sich definieren, indem man außer dem [default]-Abschnitt noch weitere Abschnitte, z.B. [rock] anlegt, dort ein anderes Verzeichnis, z.B. ein Unterverzeichnis, angibt und die Musikdateien in dieses Verzeichnis legt. Reload nicht vergessen!

3. queues.conf

In der queues.conf werden Warteschlangen definiert und entsprechende Einstellungen vorgenommen. Die Datei ist - wie Sie das schon kennen - in Abschnitte unterteilt.

Unter [general] stellen wir nur persistentmembers = yes ein, damit beim Neustart von Asterisk die Agenten automatisch wieder in ihre Schlangen aufgenommen werden.

Jede Warteschleife stellt einen eigenen Abschnitt dar. Wir legen bespielsweise eine Schlange im Abschnitt [support] an. Folgende Parameter stehen zur Verfügung:

musiconhold

Legt die in musiconhold.conf definierte Musikklasse der Warteschleife fest. Wir wählen die oben angelegte Klasse default:

musiconhold = default

announce

Legt eine Audiodatei fest, die einem Agenten vor dem Beantworten eines Anrufs vorgespielt wird. Dies dient dazu, dass ein Agent, der auf mehreren Schlangen Anrufe entgegennimmt, weiß, um welche Schlange es sich handelt und z.B. den Anrufer entsprechend begrüßen kann.

;announce = schlange-support

Wenn Sie diese Einstellung verwenden, denken Sie daran, auch eine entsprechende Audiodatei im sounds/-Verzeichnis anzulegen, also z.B. schlange-support.gsm.

strategy

Legt fest, in welcher Weise und Reihenfolge die Agenten bei einem Anruf angeklingelt werden sollen:

ringall
Alle anklingeln, bis einer antwortet. (Default)
roundrobin
Der Reihe nach alle anklingeln, bis einer antwortet.
leastrecent
Das Interface anklingeln, bei dem der letzte Anruf am längsten zurückliegt.
fewestcalls
Den anklingeln, der am wenigsten Anrufe in dieser Schlange abgearbeitet hat.
random
Ein zufällig gewähltes Interface anklingeln.
rrmemory
Round-Robin mit Gedächtnis. Beginnt die Reihe bei dem, der nach dem letzten Anruf an der Reihe ist.

Es ist zu beachten, dass Agenten, für die ein niedrigerer Malus (penalty) festgelegt wurde, immer Agenten mit höherem Malus vorgezogen werden.

strategy = ringall

Die Einstellung ist abhängig von Ihren Gegebenheiten: ringall ist vielleicht lästig, die anderen Werte verursachen zusätzliche Wartezeit für den Anrufer, falls Agenten nicht ans Telefon gehen.

servicelevel

Legt die Zeit (in Sekunden) fest, in der Anrufe beantwortet sein sollen. Nur für statistische Auswertungen interessant („Wie viele Anrufe wurden innerhalb der Service-Zeit von x Sekunden beantwortet?“).

servicelevel = 60

context

Hier kann ein Context angegeben werden, in dem, wenn der Anrufer eine Extension mit nur einer Ziffer drückt, aus der Schlange rausgenommen und zu dieser Extension in diesem Context weitergeleitet wird.

context = supportschlange-Context  ; so könnten wir es einstellen

; aber wir lassen das erst mal weg, also auskommentiert:
;context = supportschlange-Context

timeout

Legt fest, wie lange (in Sekunden) ein Telefon klingeln soll, bis wir es als nicht besetzt (also Timeout) betrachten.

timeout = 15

retry

Bestimmt, wie lange (in Sekunden) gewartet werden soll, bevor erneut alle Agenten angeklingelt werden.

retry = 5

weight

Das Gewicht (Wichtigkeit) der Schlange, relativ zu anderen Schlangen. Wenn ein Agent auf mehreren Schlangen ist, werden ihm zuerst Anrufe von Schlangen mit höherem Gewicht durchgestellt. So könnte z.B. einer Notfallschlange ein höheres Gewicht gegeben werden.

weight = 0

wrapuptime

Die Wartezeit (in Sekunden), bevor ein Agent, der gerade einen Anruf beendet hat, erneut einen Anruf erhält (Default: 0).

; wir lassen unseren Agenten eine kleine Verschnaufpause, um
; einen Schluck Wasser zu trinken:
wrapuptime = 10

maxlen

Maximale Anzahl der in der Schlange wartenden Anrufer (Default: 0 für unbegrenzt).

maxlen = 0

announce-frequency

Bestimmt, in welchen Abständen (in Sekunden) den Anrufern ihre Position in der Schlange und/oder die geschätzte Wartezeit angesagt werden soll (0 für aus).

announce-frequency = 90

announce-holdtime

Ob die geschätzte Wartezeit in den Ankündigungen nach der Position genannt werden soll. Mögliche Werte sind yes (ja), no (nein) oder once (nur einmal).

announce-holdtime = yes

announce-round-seconds

Rundungsschritte der Sekunden. Mit der Einstellung 0 werden nur die Minuten, nicht die Sekunden angesagt. Andere mögliche Werte sind: 0, 1, 5, 10, 15, 20 und 30. (Mit 30 würde beispielsweise eine geschätzte Wartezeit von 2:34 auf 2:30 gerundet.)

announce-round-seconds = 0

Sprachbausteine

Die folgenden Parameter stellen ein, welche Sprachbausteine für die Ansagen der Position und Wartezeit verwendet werden. I.d.R sind hier keine Veränderungen notwendig:

queue-youarenext = queue-youarenext     ; "You are now first in line."
queue-thereare = queue-thereare         ; "There are" ...
queue-callswaiting = queue-callswaiting ; ... "calls waiting."
queue-holdtime = queue-holdtime         ; "The current est. holdtime is" ...
queue-minutes = queue-minutes           ; ... "minutes"
queue-seconds = queue-seconds           ; ... "seconds"
queue-thankyou = queue-thankyou         ; "Thank you for your patience."
queue-lessthan = queue-less-than        ; ... "less than" ...
queue-reporthold = queue-reporthold     ; "Hold time" ...
periodic-announce = queue-periodic-announce  ; "All reps busy, wait for next"

Wenn diese Parameter nicht angegeben werden (also auskommentiert sind), gelten diese Default-Werte.

periodic-announce-frequency

Bestimmt, in welchen Abständen (in Sekunden) den Anrufern die periodische Ankündigung (periodic-announce, s.o., „Zur Zeit sind alle Mitarbeiter im Gespräch, bitte warten Sie.“) angesagt werden soll.

periodic-announce-frequency = 60

monitor-format

Die Angabe dieses Parameters schaltet die Aufzeichnung von Gesprächen ein (wie mit der Monitor()-Applikation) und bestimmt das Aufzeichnungsformat. (Bei auskommentiertem monitor-format werden keine Mitschnitte gemacht.) Geben Sie hier also gsm, wav (große Dateien!) oder wav49 an.

; wenn Sie Aufzeichnungen wollen, diese Zeile einkommentieren:
;monitor-format = gsm

Per Default werden die Dateien (eine -in und eine -out) nach ${UNIQUEID} benannt. Sie können das bei Bedarf verändern, indem Sie im Dialplan vor dem Queue()-Aufruf Set(MONITOR_FILENAME=dateiname) angeben. Wir nehmen aber im Beispiel keine Veränderung vor.

Weitere Informationen über Mitschnitte siehe Monitor()

monitor-join

Kombiniert die beiden durch einen Gesprächs-Mitschnitt erzeugten Dateien ...-in und ...-out zu einer Datei. Werte: yes oder no.

monitor-join = yes

Weitere Informationen über Mitschnitte siehe Monitor()

joinempty

Legt fest, ob Anrufer in eine Warteschleife ohne Agenten eingereiht werden.

yes
Anrufer können in eine Schleife ohne Agenten oder von nur nicht-verfügbaren Agenten eingereiht werden.
no
Anrufer können nicht in eine Schleife ohne Agenten eingereiht werden.
strict

Anrufer können nicht in eine Schleife ohne Agenten eingereiht werden (aber in eine Schlange mit nur nicht-verfügbaren Agenten).

Nicht-verfügbar ist nicht zu verwechseln mit im Gespräch (busy). Ein Agent ist nicht verfügbar, wenn er zwar der Schlange zugewiesen, aber tatsächlich nicht am System registriert ist (siehe auch member). Achtung: Statisch definierte Mitglieder (siehe member) gelten immer als verfügbar!

Sollte ein Anrufer nicht in eine Schlange aufgenommen werden, ist die Queue()-Applikation beendet, und der Dialplan wird fortgesetzt.

joinempty = no  ; wir wollen unsere Anrufer nicht unnötig warten lassen

leavewhenempty

Legt fest, ob Anrufer aus einer Schlange entfernt werden, sobald sich alle Agenten ausgeloggt haben. Mögliche Werte wie bei joinempty. Nach dem Verlassen der Schlange wird der Dialplan fortgesetzt.

leavewhenempty = strict  ; Anrufer nicht unnötig warten lassen

eventwhencalled

Stellt ein (yes|no), ob u.a. die folgenden Events für das Manager-Interface erzeugt werden: AgentCalled, AgentDump, AgentConnect, AgentComplete.

eventwhencalled = yes

eventmemberstatus

Stellt ein (yes|no), ob QueueMemberStatus-Events im Manager-Interface erzeugt werden (das können viele sein!).

eventmemberstatus = no

reportholdtime

Legt fest (yes|no), ob dem Agenten die Wartezeit des Anrufers angesagt werden soll, bevor dieser zu ihm durchgestellt wird. (Geschmacksfrage)

reportholdtime = no

memberdelay

Stellt ein, wie lange (in Sekunden) der Anrufer Stille hört, bevor er mit einem Agenten verbunden wird.

memberdelay = 1

timeoutrestart

Bestimmt, ob der Antwort-Timeout eines Agenten bei einem Besetzt- oder Leitung-überlastet-Signal zurückgesetzt wird. Kann nützlich für Agenten sein, die die Berechtigung haben, einen Anruf abzuweisen.

timeoutrestart = yes

autopause

Dieser Parameter legt fest, ob ein Agent, der einen Anruf nicht beantwortet, automatisch pausiert werden soll.

autopause = no

ringinuse

Legt fest, ob auch solche Agenten angeklingelt werden sollen, von denen bekannt ist, dass sie gerade im Gespräch sind. Der bisher einzige Channel driver, der diesen Status übermittelt, ist SIP.

ringinuse = no

member

Es ist möglich, direkt in der queues.conf Agenten statisch in der Form

member => Technologie/Resource[,Malus]

- also z.B. member => Zap/2 - anzulegen (darf mehrmals verwendet werden, siehe queues.conf). Das kann jedoch zu Problemen mit joinempty und leavewhenempty führen, da diese Agenten immer als verfügbar angesehen werden, auch wenn sie in Wirklichkeit nicht an ihrem Apparat sind. Zudem hat es den Nachteil, dass ein Agent immer fest einem Apparat zugeordnet ist und sich nicht von einem anderen Apparat aus anmelden kann.

Wir verwenden daher lieber die dynamische Form und ordnen unserer Schlange support in der Form

member => Agent/AgentenNr

zwei Agenten 1001 und 1002 zu:

member => Agent/1001
member => Agent/1002

Diese Agenten müssen wir noch in der agents.conf definieren (s.u.)[120]. Die AgentenNr ist eine frei wählbare Nummer, die jeden Agenten eindeutig identifiziert. Sie hat nichts mit den Extensions zu tun, die die Agenten evtl. verwenden.

4. agents.conf

In der agents.conf werden die Agenten, also Call-Center-Mitarbeiter, die auch „Members“ genannt werden, definiert und entsprechende Einstellungen vorgenommen. Auch diese Datei ist wieder in Abschnitte unterteilt.

Unter [general] stellen wir persistentagents = yes ein, damit die Logins der Agenten persistent (dauerhaft) sind und in der Asterisk-Datenbank gespeichert werden. Die Agenten werden dadurch beim Neustart von Asterisk automatisch wieder in ihre Schlangen aufgenommen.

Im Abschnitt [agents] nehmen wir weitere Einstellungen vor und definieren dann die Agenten. Folgende Parameter stehen zur Verfügung:

autologoff

Hiermit können wir einstellen, wie lange (in Sekunden) ein Telefon klingeln muss, bis der Agent automatisch ausgeloggt wird.

Beachten Sie, dass Agenten nicht ausgeloggt werden, wenn diese Dauer über dem timeout (timeout) der Schlange liegt! Da haben wir 15 Sekunden eingestellt, also wählen wir hier 14.

autologoff = 14

ackcall

Stellt ein (yes|no), ob Agenten, die mit AgentCallbackLogin() eingeloggt wurden, die Raute (#) drücken müssen, um ein Gespräch anzunehmen. Es ist zu beachten, dass in bisherigen Asterisk-Versionen der Voiceprompt dem Agenten nicht mitgeteilt hat, dass er # drücken muss.

ackcall = no

endcall

Stellte ein (yes|no), ob Agenten Anrufe durch Drücken der Stern-Taste (*) beenden können.

endcall = yes

wrapuptime

Legt (wie wrapuptime in der queues.conf, die doppelte Angabe ist nicht klar) die Wartezeit fest, bis ein Agent, der gerade einen Anruf beendet hat, erneut einen Anruf erhält - hier allerdings in Millisekunden. Default: 5000.

wrapuptime = 5000   ; 5 Sekunden Pause

musiconhold

Legt die Music-on-Hold-Klasse für die Agenten fest.

musiconhold = default

updatecdr

Den CDR-Record anpassen (yes|no), so dass die Kanalangabe Agent/AgentenNr enthält, damit wir wissen, welcher Agent das Gespräch geführt hat.

updatecdr = yes

recordagentcalls

Gespräche mit Agenten aufzeichnen (yes|no). Default: no.

recordagentcalls = no

recordformat

Aufzeichnungsformat für Mitschnitte. gsm, wav (große Dateien!) oder wav49. Default: wav.

recordformat = gsm

createlink

Legt fest (yes|no), ob im User-field (dem Freitextfeld) des CDR der Dateiname der Aufzeichnung gespeichert werden soll, damit man sie später zuordnen kann.

createlink = yes

urlprefix

Wenn Sie createlink verwenden, kann mit urlprefix direkt eine URL vor den Dateinamen gesetzt werden, unter der Sie die Aufzeichnungen mit einem Webserver im Intranet verfügbar gemacht haben.

; so könnte ein Beispiel aussehen:
;urlprefix = http://astbox/anrufe/

savecallsin

Hier können Sie angeben, in welchem Verzeichnis die Mitschnitte gespeichert werden sollen. Default: /var/spool/asterisk/monitor

; wenn wir z.B. die Mitschnitte in /var/calls/ speichern wollen:
;savecallsin = /var/calls

agent

agent wird zur Definition von Agenten verwendet (kann mehrfach vorkommen).

; Format: agent => AgentenNr,Passwort,Name
agent => 1001,0000,Mark Spencer
agent => 1002,0000,Will Meadows

5. extensions.conf

Im Dialplan (Kapitel 4, Programmieren im Dialplan) können sich zum einen die Agenten über eine bestimmte Extension in die Warteschleife einloggen, zum anderen werden Anrufe mit der Applikation Queue() (Queue()) in eine Schlange eingereiht.

Eine minimale Extension, die die Anrufer in die Warteschleife einreiht, sähe so aus:

exten => 20,1,Answer()                   ; abheben
exten => 20,n,Set(MUSICCLASS()=default)  ; Musikklasse "default" setzen
exten => 20,n,Queue(support,t)           ; in Schlange "support" einreihen
exten => 20,n,Hangup()                   ; hinterher auflegen

Wir lieben es etwas vollständiger und verwenden diese Extension:

exten => 20,n,Wait(2)
exten => 20,n,Answer()                   ; abheben
exten => 20,n,Set(MUSICCLASS()=default)  ; Musikklasse "default" setzen
exten => 20,n,Queue(support,t)           ; in Schlange "support" einreihen
; zur Erinnerung: die Option t erlaubt es dem Agenten, den Anruf
; an eine andere Extension weiterzuleiten
; setzt QUEUESTATUS =
; TIMEOUT | FULL | JOINEMPTY | JOINUNAVAIL | LEAVEEMPTY | LEAVEUNAVAIL
exten => 20,n,Goto(q-${QUEUESTATUS},1)  ; je nach QUEUESTATUS springen

; keine Agenten auf der Schlange:
; (hier könnte man alternativ auf VoiceMail() leiten)
exten => q-JOINEMPTY,1,Wait(1)
exten => q-JOINEMPTY,n,Playback(vm-nobodyavail,noanswer)
exten => q-JOINEMPTY,n,Playback(vm-goodbye,noanswer)
exten => q-JOINEMPTY,n,Hangup()

; keine Agenten (oder nur nicht-verfügbare) auf der Schlange:
exten => q-JOINUNAVAIL,1,Goto(q-JOINEMPTY,1)   ; wie JOINEMPTY behandeln

; alle Agenten haben Schlange verlassen:
exten => q-LEAVEEMPTY,1,Goto(q-JOINEMPTY,1)    ; wie JOINEMPTY behandeln

; alle Agenten (auch nicht-verfügbare) haben Schlange verlassen:
exten => q-LEAVEUNAVAIL,1,Goto(q-JOINEMPTY,1)  ; wie JOINEMPTY behandeln

; kein Agent antwortet:
exten => q-TIMEOUT,1,Goto(q-JOINEMPTY,1)       ; wie JOINEMPTY behandeln

; max. Anruferzahl für Schlange erreicht:
; (hier könnte man alternativ auf VoiceMail() leiten)
exten => q-FULL,1,Busy(5)
exten => q-FULL,n,Hangup()

Wenn wir diese Extension anrufen, werden wir, da keine Agenten angemeldet sind, die Ansage hören, dass z.Zt. niemand unseren Anruf entgegennehmen kann. (Wenn Sie das verschleiern wollen, setzen Sie in der queues.conf die Parameter joinempty=yes und leaveempty=no.)

Es fehlt also noch eine Extension, unter der sich die Agenten einloggen, d.h. anwesend melden:

exten => 25,1,Answer()       ; abheben
exten => 25,n,AgentLogin()   ; Agent einloggen
exten => 25,n,Hangup()       ; hinterher auflegen

Mit AgentLogin() bleibt die Verbindung zum Agenten dauerhaft bestehen, und die eingehenden Anrufe werden auf diesen bereits aufgebauten Sprachkanal aufgeschaltet. Wenn der Agent den Hörer auflegt, wird er abgemeldet. Das bedeutet auch, dass ein Agent nicht gleichzeitig eingeloggt sein kann und ausgehende Gespräche machen kann. AgentLogin() ist daher in der Praxis wirklich nur dann geeignet, wenn es sich um ein reines Inbound-Call-Center handelt.

AgentCallbackLogin() hingegen hat den Vorteil, dass sich Agenten einmal anmelden können und bei eingehenden Gesprächen zurückgerufen werden. Sie bleiben also auch beim Auflegen angemeldet und können ausgehende Gespräche führen. Diese Applikation ist aber seit Version 1.4 „deprecated“, wird also zukünftig nicht mehr vorhanden sein.

Digium verwendet im Beispiel doc/queues-with-callback-members.txt (in AEL, lässt sich aber leicht in einen „herkömmlichen“ Dialplan umwandeln)

Read(agentennr,agent-user);
VMAuthenticate(${agentennr},s);

zum Authentifizieren der Agenten, AddQueueMember() zum dynamischen Hinzufügen zur Schlange und Dial() zum Anrufen der Agenten.

Sollte AgentCallbackLogin() tatsächlich ersatzlos wegfallen, geht damit tatsächlich Funktionalität und das leichte Setup verloren. (An dieser Stelle nochmal vielen Dank an Markus Bönke!)

Aufgabe. Es bleibt dem Leser als Aufgabe überlassen, vor die Einreihung in eine Warteschlange noch ein Menü zu schalten, z.B. „Um eine Bestellung aufzugeben, drücken Sie die 1. Für Reklamationen drücken Sie die 2.“, und dann zwei Warteschlangen einzurichten, denen die Agenten der entsprechenden Abteilung angehören.

6. Log-Datei

Ereignisse, die auf den Warteschlangen auftreten, werden im Queue-Log, normalerweise /var/log/asterisk/queue_log detailliert festgehalten (natürlich werden auch Einträge im CDR-Log gemacht). Im queue_log steht ein Eintrag pro Zeile im Format:

Timestamp|Anruf-ID|Schlange|Kanal|Event|Param1[|Param2[|Param3]]
Timestamp
Ist die Unix-Zeit, zu der der Event aufgetreten ist.
Anruf-ID
Ist die einmalige ID des Anrufs (alphanumerisch).
Schlange
Ist der Name der Warteschlange, z.B. support. Kann auch NULL sein.
Kanal
Ist der Name des überbrückten Kanals, z.B. Agent/1001. Kann auch NULL sein.
Event
Ist der Name des aufgetretenen Ereignisses (s.u.). Abhängig vom Event sind Param1, Param2 und Param3 angegeben.

Die möglichen Ereignisse sind u.a. (siehe auch doc/queuelog.txt):

ABANDON
Anrufer hat seine Position in der Schlange durch Auflegen aufgegeben. Parameter: Position, Einstiegsposition, Wartezeit.
AGENTDUMP
Agent hat den Anrufer während der Ankündigung der Schlange abgewiesen.
AGENTLOGIN
Agent hat sich eingeloggt. Parameter: Kanal (z.B. SIP/127.0.0.1-0181ac00).
AGENTCALLBACKLOGIN
Callback-Agent hat sich eingeloggt. Parameter: Login-Extension[@Context].
AGENTLOGOFF
Agent hat sich ausgeloggt. Parameter: Kanal, Login-Dauer.
AGENTCALLBACKLOGOFF
Agent hat sich (/wurde) ausgeloggt. Parameter: Login-Extension[@Context], Login-Dauer, Grund (z.B. Autologoff).
COMPLETEAGENT
Gespräch zwischen Anrufer und Agent wurde vom Agenten beendet. Parameter: Wartezeit, Anruf-Dauer, Einstiegsposition.
COMPLETECALLER
Gespräch zwischen Anrufer und Agent wurde vom Anrufer beendet. Parameter: Wartezeit, Anruf-Dauer, Einstiegsposition.
CONFIGRELOAD
Konfiguration wurde neu eingelesen (z.B. durch asterisk -rx "reload").
CONNECT
Anrufer wurde zu einem Agenten durchgestellt. Parameter: Wartezeit.
ENTERQUEUE
Anrufer wurde in die Schlange aufgenommen. Parameter URL (falls verfügbar), Caller-ID.
EXITEMPTY
Anrufer wurd aus der Schlange geworfen, weil keine Agenten verfügbar waren[121]. Parameter: Position zum Zeitpunkt des Verlassens der Schlange, ursprüngliche Position beim Eintritt, Wartezeit.
EXITWITHKEY
Anrufer hat die Schlange durch Drücken einer Taste verlassen. Parameter: Taste, Position.
EXITWITHTIMEOUT
Anrufer war zu lange in der Schlange, und der Timeout ist abgelaufen. Parameter: Position.
QUEUESTART
Das Warteschlangensystem wurde gestartet. Dabei haben die Felder Anruf-ID, Schlange und Kanal den Wert NULL.
RINGNOANSWER
Ein verfügbarer Agent wurde angeklingelt, hat aber nicht abgenommen (Timeout). Parameter: Klingeldauer (in Millisekunden).
SYSCOMPAT
Agent hat Anruf angenommen, aber die Kanäle waren nicht kompatibel, und der Anruf wurde beendet.
TRANSFER
Anrufer wurde auf eine andere Extension umgeleitet. Parameter: Extension, Context.

Kommerzielle Log-Analyse- und Echtzeitüberwachungssysteme sind QueueMetrics[122] oder Easy PABX[123]. Siehe auch QueueLog().

Asterisk kann derzeit das Queue-Log noch nicht direkt in eine SQL-Tabelle schreiben. Wie man das trotzdem erreichen kann, wird auf http://www.voip-info.org/wiki/view/Asterisk+queue_log+on+MySQL beschrieben. Eine Möglichkeit ist, die Datei queue_log durch eine Named-Pipe zu ersetzen und dann z.B. mit einem Perl-Skript die Einträge in eine Datenbank zu schreiben. Hier ein Perl-Skript von William Lloyd[124]:

#!/usr/bin/perl -w
#
# wlloyd at slap.net

# The asterisk version indpendant way to get queue stats into Mysql,  
Postgres
# or whatever is supported by Perl DBI

# It's all about named pipes

# to setup this software
# stop asterisk
# rm /var/log/asterisk/queue_log
# mkfifo /var/log/asterisk/queue_log

# make sure permissions are setup
# chmod 777 /var/log/asterisk/queue_log

# run this program as root or under another user as you see fit.
# should start BEFORE asterisk.  Add to /etc/rc.d/rc.local or whatever

# restart asterisk

# requires a DB table like the following..
# CREATE TABLE csr_queue (
#  qname varchar(30) default NULL,
#  agent varchar(30) default NULL,
#  action text,
#  info1 text,
#  info2 text,
#  info3 text,
#  timestamp int(11) NOT NULL default '0',
#  id tinytext NOT NULL
#) TYPE=MyISAM;

use DBI;
use IO::File;

my $opt_debug = 0;

# if you want postgres change this to "Pg"
my $db_type = "mysql";
my $db_host = "127.0.0.1";
my $db_user_name = 'username';
my $db_password = 'password';
my $db_database = 'asteriskstat';

my $dbh = DBI->connect("DBI:$db_type:dbname=$db_database;host= 
$db_host;", $db_user_name, $db_password);

open(FIFO, "< /var/log/asterisk/queue_log")        or die "Can't open  
queue_log : $!\n";

while (1) {

     $message = <FIFO>;
     next unless defined $message;   # interrupted or nothing logged
     chomp $message;

     # remove chars that will cause DB problems
     $message =~ s/\"\'//g;

     @data = split(/\|/,$message);

     # these messages are almost useless for my purposes
     next if ($data[4] eq "QUEUESTART" );
     next if ($data[4] eq "CONFIGRELOAD" );

     if (!defined($data[5])) {
       $data[5] = '';
     }
     if (!defined($data[6])) {
       $data[6] = '';
     }
     if (!defined($data[7])) {
       $data[7] = '';
     }

     my $sql = "INSERT INTO csr_queue (timestamp, id, qname, agent,  
action, info1, info2, info3) VALUES ('$data[0]', '$data[1]', '$data 
[2]', '$data[3]', '$data[4]', '$data[5]', '$data[6]', '$data[7]')";

     print "$sql \n\n" if ($opt_debug);

     $dbh->do($sql);

# if you want an actual logfile you might want to uncomment this
#        if ( open(LOG, ">> /var/log/asterisk/queue_log_real") ) {
#            print LOG "$message\n";
#            close(LOG);
#        } else {
#            warn "Couldn't log to /var/log/asterisk_queue_log: $!\n";
#        }
#
}

$dbh->disconnect();

exit 0;

Bei QueueMetrics (kostenlose Demo-Version) wird ebenfalls ein Perl-Skript queueLoader.pl mitgeliefert.



[116] Ein Beispiel-Setup in AEL von Digium wird ab Asterisk 1.4 in doc/queues-with-callback-members.txt beschrieben.

[117] Hier wird der Einfachheit halber der Begriff „Agenten“ verwendet, Markus Bönke macht aber folgende Anmerkung:

Sie übersetzen Queue Member mit "Agenten", wenn man aus der Call-Center-Welt kommt, ist das ein bisschen verwirrend, da Queue Members durchaus normale SIP-Extensions sein können.

[118] sox (http://sox.sourceforge.net/) gibt es als Debian-Paket, also:

$  apt-get install sox

Für MacOS X gibt es - mit installiertem Darwinports (http://darwinports.opendarwin.org/ oder http://darwinports.com/, siehe auch http://apfelwiki.de/wiki/Main/MacPorts) - auch ein Paket:

$  port install sox

[118] Auf anderen Plattformen müssen Sie die Sourcen herunterladen und kompilieren, sofern Sie keine Binaries finden.

[119] lame (http://lame.sourceforge.net/) gibt es nicht als Debian-Paket, wir müssen also die Sourcen herunterladen und mit den bekannten Befehlen ./configure, make und make install kompilieren und installieren.

[119] Für MacOS X gibt es ein Paket über Darwinports (siehe Beschreibung sox):

$  port install lame
oder über Fink (http://fink.sourceforge.net/):
$  fink install lame
Natürlich geht das auch über den FinkCommander mit grafischem Interface.

[120] sonst wären es wirklich Geheimagenten ;-)

[124] wlloyd at slap.net, veröffentlicht auf der Digium-Mailingliste asterisk-users, siehe: http://lists.digium.com/pipermail/asterisk-users/2005-July/109892.html


Version 1.2, November 2002

Neue Version verfügbar

Sie betrachten gerade die alte Version des Buches (Version 1.0). Wir empfehlen Ihnen für Asterisk 1.4 und 1.6 die neue Version des Buches.

Asterisk-Tag 2008

Lernen Sie Mark Spencer (den Erfinder von Asterisk) kennen! Viele Vorträge, Case-Studies und Workshops rund um das Thema VoIP. Asterisk-Tag.org

Das gedruckte Buch

Werbung