AF
Asterisk Forum
обсуждения телефонии, VoIP и IP-PBX
12разделов
5 423тем
34 385сообщений
← К списку тем

Получение номера, который ответил в очереди.

Asterisk IP PBX 39 сообщений -
#1

Получение номера, который ответил в очереди.


Есть такая задачка - получить номер, который снял ответил в очереди и пересылка этого номера сервису. Как я понимаю, для решения этой задачи используется AMI. Но проблема в том, что для меня программирование на PHP/Perl и иже с ними - темный лес. Скрипты на баше - еще куда ни шло Smile
Идейно, я понимаю, что будет некоторый скриптик на ПХП крутиться и слушать происходящее не порту. Как это реализовать - для меня вообще не понятно.
Вопрос какого плана: с чего начинать копать в эту область? Есть ли какие-то примеры для новичков?

_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
#3

Запрос к внешнему сервису должен быть практически одновременно со снятием трубки. Парсер логов - это хорошо, но не каждые пол-секунды.
Пока я вижу реализацию так:
Скрипт с вечным циклом висит на порту менеджера и ждет пока от * не придет EOF. Потом разбирает строчку. Если это - AgentAnswer (или что-то такое), посылаем запрос на CallerID, соотв. этому каналу. Ждем ответ. При ответе - высылаем оба номера внешнему сервису.
Готов выслушать критику Smile

_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
#4

Можно из потока по порту 5038 (manager) выделять Event: Bridge. Только формат этого сообщения поправить. Я буквально недавно об этом спрашивал http://asteriskforum.ru/viewtopic.php?t=7723
#5

2 Leon77
А как организовано висение скрипта на порту? Или он работает в формате "подключил-спросил-отключился"?

_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
#6

Я скрипт еще не делал (пока время терпит). Но ничего сложного, как я думаю, нет. Открыл соединение и слушай поток. Анализируя поток можно выполнять какие-то действия.
#7

не все так просто как вам думается. начнем с того, что эвент - событие астериск менеджера, а звонок на уровне дайлплана (или через аги). а аги и ами общего между собой ничего не имеют. звонок идет одним потоком, а события - другим. из дайлплана нельзя получить данные от менеджера. нужно продумывать весь механизм взаимодействия и для такой простой задачи нет смысла в этом.

в астериске в команде queue можно вызвать макрос или аги после поднятия трубки. вот там и делайте, насколько помню, скрипт срабатывает в момент поднятия агентом трубки, при этом соеденение еще не происходит, оно произойдет по завершению скрипта. тем не менее, в этот момент доступны все ресурсы, можно вытащить данные, кто ответил и сохранить в астдб например. не в канал, так как это временный канал, скрипта.
#8

Samael, лог очереди может храниться в mysql
_________________
Gentoo Linux || Asterisk 11.2-cert3
Решения телефонии на базе Asterisk || http://it-need.ru
#9

2 andyk74
О.. Спасибо большое. Как-то не подумал про этот момент.
2 aven
К сожалению, я плохо понимаю, как по записи параметра в sql можно сделать внешний вызов чего-либо. Плюс, как я понимаю, лог записывается ПОСЛЕ того, как трубка будет положена (чтобы время разговора записать).

_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
#10

судя по версии * топикстартера нативно хранить лог очереди в базе не получится... макросом впрочем тоже не восспользоваться из за этого. AGI запустится на канале звонящего, там тоже мало чего интересного достать можно...


так что либо проапдейтится на что то более новое либо пилить AMI или логи очереди...
#11

queues.conf

setinterfacevar=yes
; If set to yes, just prior to the caller being bridged with a queue member
; the following variables will be set
; MEMBERINTERFACE is the interface name (eg. Agent/1234)
; MEMBERNAME is the member name (eg. Joe Soap)
; MEMBERCALLS is the number of calls that interface has taken,
; MEMBERLASTCALL is the last time the member took a call.
; MEMBERPENALTY is the penalty of the member
; MEMBERDYNAMIC indicates if a member is dynamic or not
; MEMBERREALTIME indicates if a member is realtime or not
;

... а у меня вообще local channel используется, так как логика маршрутизации, заказанная клиентом просто сумасшедшая, астериск так не может.... разная продолжительность и номера агентов на каждом шаге, бакап на мобилку и т.д.

потому как раз у меня обработчик на ами + макро 'по соеденению' в команде queue:

cat ~/Dropbox/coding/voiceapp5/components/call_center/call_center.rb | grep Queue
Adhearsion::CallCenter::Queue.new
execute "Queue", "customers", "", "", "", "", "agi:\/\/127.0.0.1:4040\/connected_with_agent", "", "", ""

def connected_with_agent
set_variable 'CHANNEL(musicclass)', 'default'
SolutionNumber::Call.connected_with_agent get_variable("channel_owner")
...

это дает офигительную гибкость, но с багами из-за local channel, временами у астериска крыша едет...
#12

2 andyk74: это все замечательно, но эти переменные ставятся _после_ соединения, при выполнения макроса в очереди соедиенеия еще нет и переменные пустые. Помоему единственное, как макрос топикстартеру может помочь - вытащить CHANNEL и распарсить его, с некоторой долей вероятности этого автору хватит.

я не вижу как в отгрепанном аги коде Вы получаете номер ответившего агента. Про то, что это можно сделать в AMI я не спорю.
#13

не знаю о том не о том Smile немного другая задача стояла, но получить номер кто ответил тоже былаSmile
Код:

....
Queue(queue_fins_udp,tw,,,,queue_udp.pl,ANSWER);
....


ну и код
Код:
#!/usr/bin/perl -w

use strict;
use Asterisk::AGI;
my $AGI=new Asterisk::AGI;

my $tmp;
my $tmp2;
my $frec;
my @temp; #param explode

$tmp=$AGI->get_variable('CDR(dstchannel)'); # достаем имя канала назанчения
#$tmp2=$AGI->get_variable('CDR(userfield)'); # для опытов :)
$frec = $AGI->get_variable('file');

@temp=split(/-/,$tmp); # немного мистики :)
@temp=split(/\//,$temp[0]); # и еще чуть чуть Вуаля и тут номер кто ответил :)
$AGI->verbose($temp[1],1); # мусорим в консоль номером ответившего
$AGI->verbose($frec,1); # мусорим в консоль имя файла записи :)

$AGI->database_put('callcenter', $temp[1], $frec); # это в базу астериска ключ пишеться

exit;


_________________
нанотехнолигии в области Asterisk


Последний раз редактировалось: Cache (Пт Мар 25, 2011 18:23)
#14

прикольно. в CDR (), в середине звонка, я чегото поглядеть не подумал...
#15

Игорь писал(а):
в середине звонка,...


не в середине, а в самый момент ответа Smile

_________________
нанотехнолигии в области Asterisk
#16

Игорь писал(а):
2 andyk74: это все замечательно, но эти переменные ставятся _после_ соединения, при выполнения макроса в очереди соедиенеия еще нет и переменные пустые. Помоему единственное, как макрос топикстартеру может помочь - вытащить CHANNEL и распарсить его, с некоторой долей вероятности этого автору хватит.

я не вижу как в отгрепанном аги коде Вы получаете номер ответившего агента. Про то, что это можно сделать в AMI я не спорю.


в adhearsion ами и аги имеют-таки точки соеденения, вот эта функция (connected_with_agent), как раз и вызывает метод, который на основе канала (ответившего) и основного канала, чарез АМИ устанавливает переменные. навороченно, но в моем случае оправданно, так как у меня много действий с менеджером, я вынужден следить за состоянием каналов. код могу дать.
#17

Спасибо. Попробую реализовать то же самое на ПХП. О результатах напишу Smile
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
#18

Все великолепно работает, если писать лог очереди в БД и разбирать по триггеру, который будет дергать нужную процедуру скажем.
_________________
e=mc^2
#19

Всё ещё хуже


andyk74 писал(а):
не все так просто как вам думается. начнем с того, что эвент - событие астериск менеджера, а звонок на уровне дайлплана (или через аги). а аги и ами общего между собой ничего не имеют. звонок идет одним потоком, а события - другим. из дайлплана нельзя получить данные от менеджера. нужно продумывать весь механизм взаимодействия и для такой простой задачи нет смысла в этом.

в астериске в команде queue можно вызвать макрос или аги после поднятия трубки. вот там и делайте, насколько помню, скрипт срабатывает в момент поднятия агентом трубки, при этом соеденение еще не происходит, оно произойдет по завершению скрипта. тем не менее, в этот момент доступны все ресурсы, можно вытащить данные, кто ответил и сохранить в астдб например. не в канал, так как это временный канал, скрипта.



3600 - клиент
3890 - очередь в которой один peer 3705 (его мы и должны обнаружить).

В extensons.conf
exten => 3890,1,Answer()
exten => 3890,n,Queue(3890,rtT,,,60,test1.sh)


cat test1.sh
#!/bin/bash -x
cat > /tmp/test_agi.txt
exit 0

Из AMI делаем звонок:
Action: Originate
Channel: DAHDI/g1/3600
Exten: 3890
Context: voip
Priority: 1
ActionID: 12345678

Response: Success
ActionID: 12345678
Message: Originate successfully queued

Поднимаем на клиенте, после этого приходит звонок на 3705
Смотрим /tmp/test_agi.txt

[root@AVD-ats tmp]# cat test_agi.txt
agi_request: test1.sh
agi_channel: DAHDI/4-1
agi_language: ru
agi_type: DAHDI
agi_uniqueid: 1301486869.61307
agi_version: 1.6.2.13
agi_callerid: unknown
agi_calleridname: unknown
agi_callingpres: 0
agi_callingani2: 0
agi_callington: 0
agi_callingtns: 0
agi_dnid: unknown
agi_rdnis: unknown
agi_context: voip
agi_extension: 3890 !!!!!!
agi_priority: 2
agi_enhanced: 0.0
agi_accountcode:
agi_threadid: 22748048


Sad И здесь нет номера агента....
#20

Re: Всё ещё хуже


скину код на руби под adhearsion, который работает. через пару часов.
#21

Re: Всё ещё хуже


Спасибо, уже работает ..... завтра отпишусь ... Very Happy
#22

Цитата:
в астериске в команде queue можно вызвать макрос или аги после поднятия трубки. вот там и делайте, насколько помню, скрипт срабатывает в момент поднятия агентом трубки, при этом соеденение еще не происходит, оно произойдет по завершению скрипта. тем не менее, в этот момент доступны все ресурсы, можно вытащить данные, кто ответил и сохранить в астдб например. не в канал, так как это временный канал, скрипта.

Кстати почему-то данный макрос у меня не работал, помнится мне, что в него не так просто было передать переменные.

_________________
e=mc^2
#23

Давайте разберем. Вот лог, это одновременно AMI и AGI. Итак, звонок пришел с од ного pbx'a на другой;

Пошло событие AgentCalled;
Агент поднял трубку, запустился скрипт AGI: agi://127.0.0.1:4040/bridged_with_agent

На уровне AGI, система пока не знает об агенте. Так как соеденения еще нет.
Оно произойдет после того, как скрипт отработает.

Итак, он завершился, в менеджере прошли события: AgentConnect, Bridge.

Теперь о действии. В колл-центре это может быть на всех трех уровнях:

Звонок идет, аганта телефон звонит - AgentCalled. В событии менеджера есть вся необходимая информация, можно вызвать внешнее событие, ход звонка продолжится параллельно.

Агент ответил, но нужно какое-то действие, информация на его экране. Это на уровне AGI. Пока agi://127.0.0.1:4040/bridged_with_agent не завершит работу, звонящему идут гудки. Потому тут важно информацию обработать быстро.

И последнее, событие по AgentConnect или Bridge. Агент уже ответил и начал говорить 'здравствуйте'

Важно - события в астериск менеджере надо обрабатывать не задерживаясь. Тайм-аут просто вызовет их прерывания и что-то элементарно не дойдет до обработчика. Если надо работать с базой или внешним скриптом, часто будет разумно вызвать их отдельным потоком:
Thread.start { фоновое-действие }

Чуть позднее напишу и пришлю AGI для всех трех вариантов.

Код:
[Thu Mar 31 14:42:45 2011] Adhearsion::VoIP::Asterisk::AGI::Server::RubyServer 0.0.0.0:4040 start
INFO events: #"home_pbx_trunk",
"Exten"=>"137",
"ChannelStateDesc"=>"Down",
"Uniqueid"=>"pbx.lan-1301582569.619",
"ChannelState"=>"0",
"Channel"=>"SIP/home_pbx_trunk-000001bc",
"Context"=>"incoming",
"AccountCode"=>"",
"CallerIDName"=>"k@home",
"Privilege"=>"call,all"},
@name="Newchannel">

INFO events: #"",
"Exten"=>"",
"ChannelStateDesc"=>"Down",
"Uniqueid"=>"pbx.lan-1301582569.620",
"ChannelState"=>"0",
"Channel"=>"SIP/k-000001bd",
"Context"=>"outgoing",
"AccountCode"=>"",
"CallerIDName"=>"",
"Privilege"=>"call,all"},
@name="Newchannel">

INFO events: #"137",
"CallerIDNum"=>"home_pbx_trunk",
"DestinationChannel"=>"SIP/k-000001bd",
"AgentCalled"=>"SIP/k",
"Uniqueid"=>"pbx.lan-1301582569.619",
"ChannelCalling"=>"SIP/home_pbx_trunk-000001bc",
"Context"=>"incoming",
"AgentName"=>"SIP/k",
"Priority"=>"1",
"CallerIDName"=>"k@home",
"Privilege"=>"agent,all",
"Queue"=>"pbx"},
@name="AgentCalled">

[Thu Mar 31 14:42:52 2011] Adhearsion::VoIP::Asterisk::AGI::Server::RubyServer 0.0.0.0:4040 client:33241 localhost.localdomain connect
DEBUG agi: Handling call with variables {:type_of_calling_number=>:unknown, :type=>"SIP", :dnid=>137, :uniqueid=>"pbx.lan-1301582569.619", :callingtns=>0, :priority=>1, :threadid=>-1246483568, :accountcode=>"", :extension=>137, :network_script=>"bridged_with_agent", :calleridname=>"k@home", :language=>"en", :version=>"1.6.2.16.1", :request=>#, :rdnis=>nil, :callerid=>"home_pbx_trunk", :query=>{}, :network=>true, :enhanced=>0.0, :callingani2=>0, :context=>"incoming", :callingpres=>0, :channel=>"SIP/home_pbx_trunk-000001bc"}
INFO agi: "SIP/home_pbx_trunk-000001bc"

[Thu Mar 31 14:42:52 2011] Adhearsion::VoIP::Asterisk::AGI::Server::RubyServer 0.0.0.0:4040 client:33241 disconnect
INFO events: #"SIP/k-000001bd",
"Uniqueid"=>"pbx.lan-1301582569.619",
"Holdtime"=>"3",
"Member"=>"SIP/k",
"Ringtime"=>"2",
"MemberName"=>"SIP/k",
"Privilege"=>"agent,all",
"BridgedChannel"=>"pbx.lan-1301582569.620",
"Queue"=>"pbx"},
@name="AgentConnect">

INFO events: #"home_pbx_trunk",
"CallerID2"=>"home_pbx_trunk",
"Channel1"=>"SIP/home_pbx_trunk-000001bc",
"Bridgetype"=>"core",
"Channel2"=>"SIP/k-000001bd",
"Uniqueid1"=>"pbx.lan-1301582569.619",
"Uniqueid2"=>"pbx.lan-1301582569.620",
"Privilege"=>"call,all",
"Bridgestate"=>"Link"},
@name="Bridge">

INFO events: #"3",
"Channel"=>"SIP/k-000001bd",
"Uniqueid"=>"pbx.lan-1301582569.619",
"Reason"=>"agent",
"Member"=>"SIP/k",
"TalkTime"=>"3",
"MemberName"=>"SIP/k",
"Privilege"=>"agent,all",
"Queue"=>"pbx"},
@name="AgentComplete">

INFO events: #"16",
"CallerIDNum"=>"home_pbx_trunk",
"Cause-txt"=>"Normal Clearing",
"Uniqueid"=>"pbx.lan-1301582569.620",
"Channel"=>"SIP/k-000001bd",
"CallerIDName"=>"k@home",
"Privilege"=>"call,all"},
@name="Hangup">

INFO events: #"0",
"CallerIDNum"=>"home_pbx_trunk",
"Cause-txt"=>"Unknown",
"Uniqueid"=>"pbx.lan-1301582569.619",
"Channel"=>"SIP/home_pbx_trunk-000001bc",
"CallerIDName"=>"k@home",
"Privilege"=>"call,all"},
@name="Hangup">
#24

Кстати почти в тему:

Делаю так:

exten => 1,1,Answer
exten => 1,n,Queue(test,,http://192.168.0.180/RID=${UNIQUEID}&CallerId=${CALLERID(num)},,,/root/11.pl,ohh)

[macro-ohh]
exten => s,1,NoOP(---${UNIQUEID}---${CALLERID(num)}-----${CDR(src)} )

И вижу в CLI:
Код:

-- Executing [s@test-queue:4] NoOp("SIP/202-00000010", "202") in new stack
-- Executing [s@test-queue:5] Queue("SIP/202-00000010", "test,,http://192.168.0.180/RID=1308226954.27&CallerId=202,,,/root/11.pl,ohh") in new stack
-- Started music on hold, class 'default', on SIP/202-00000010
-- Stopped music on hold on SIP/777-0000000f
-- agent_call, call to agent '11' call on 'SIP/777-0000000f'
-- Playing 'beep.gsm' (language 'en')
-- Called Agent/11
[Jun 16 16:22:36] NOTICE[22762]: res_rtp_asterisk.c:2190 ast_rtp_read: Unknown RTP codec 72 received from '192.168.0.1:58926'
-- SIP/777-0000000f acknowledged
-- Agent/11 answered SIP/202-00000010
[Jun 16 16:22:38] NOTICE[22762]: app_queue.c:4641 try_calling: Delaying member connect for 1 seconds
-- Stopped music on hold on SIP/202-00000010
-- Executing [s@macro-ohh:1] NoOp("Agent/11", "---1308226954.28---111-----202 ") in new stack

Т.е. в случае вывода переменной CALLERID(num) в строке Queue вижу номер с которого звонят (202 в данном примере), а в макро эта переменная становится в другое значение (111 - номер тел. очереди) и приходится правильный номер звонящего выцеплать из CDR-переменной.

Чудеса Smile

_________________
P4 3.0 + 1Gb CentOS 5.8 Aster 1.8.16
Не люблю gui-сборки: натуральный продукт вкуснее.
И еще: я ПРОФИ так как НЕ ЛЕНЮСЬ читать литературу.
#25

Может кому пригодится, на Asterisk 1.8.7 из макроса, запущенного через Queue переменная ${CDR(src)} равнялась номеру клиента. В версии 1.8.10 равнялась номеру агента очереди.
Вывести номер клиента удалось только с помощью ${CONNECTEDLINE(num)}
#26

че то я читаю читаю, и не пойму, как правильно вписать в exstentions.conf - memberinterface
нужно, чтобы номер ответившего в группе отображался в записанном файле

queues.conf
[managers]
strategy = rrmemory
timeout = 20
retry = 1
member => SIP/3653
member => SIP/3654
member => SIP/3655
member => SIP/3656
member => SIP/3657
setinterfacevar=yae

exstentions.conf
exten => 3652,1,Set(fname=${CALLERID(num)}--${EXTEN}--${STRFTIME(${EPOCH},,%d.%m.%Y_%H-%M-%S)})-$
exten => 3652,2,Set(MONITOR_EXEC=/usr/local/bin/lame -b 16 --silent "/wav/${fname}.wav" "/mnt/3652/${fname}.mp3" && rm -f "/wav/${fname}.wav")
exten => 3652,3,MixMonitor(/wav/${fname}.wav|b|${MONITOR_EXEC})
exten => 3652,4,Answer
;exten => 3652,5,Wait(1)
exten => 3652,5,Queue(managers|t|||105);Общий таймаут для очереди, т.е. через сколько времени покинуть очередь вообще
;Таймаут перехода между операторами. Задается в файлике queues.conf (Параметр timeout)
exten => 3652,6,Background(spravka)
exten => 3652,7,VoiceMail(3684)
exten => 3652,8,Hangup

exten => 3653,1,Set(fname=${CALLERID(number)}--${EXTEN}--${STRFTIME(${EPOCH},,%d.%m.%Y_%H-%M-%S)})-$
exten => 3653,2,Set(MONITOR_EXEC=/usr/local/bin/lame -b 16 --silent "/wav/${fname}.wav" "/mnt/3652/${fname}.mp3" && rm -f "/wav/${fname}.wav")
exten => 3653,3,MixMonitor(/wav/${fname}.wav|b|${MONITOR_EXEC})
exten => 3653,4,Dial(SIP/3653/${EXTEN},120)
exten => 3653,5,PlayBack(invalid)
exten => 3653,6,HangUp
#28

подскажите пожалуйста, в каком месте мне надо вставить
exten => h,n,NoOp( == ${CUT(MEMBERINTERFACE,/,2)} == )
чтобы номер ответившего отобразился в имени файла
#29

Роман1111 Вы вообще понимаете что делает NoOp ?
#30

отображение в консоли инфы

Added after 10 minutes:

я вот это ${CUT(MEMBERINTERFACE,/,2)} вставляю в разные места extension.conf, что то не реагирует никак
#31

Куда именно вы его вставляете?
Согласно логике вещей его наверное надо вставить в формирование имени файла.
#32

туда и ставлю, в номер группы 3652
exstentions.conf
exten => 3652,1,Set(fname=${CALLERID(num)}--${CUT(MEMBERINTERFACE,/,2)}--${STRFTIME(${EPOCH},,%d.%m.%Y_%H-%M-%S)})-$
и в файле на это месте получается --
#33

Версия какая? Щас у себя попробую проверить.
#34

Asterisk 1.4.21.2
#35

Насколько мне помнится в 1.4 очереди не работают с макросами. Вам нужен вариант 1 из того что awsswa привел. И обновить астериска.
#36

без макроса в моем варианте номер не выдернуть?
так то очереди работают без проблем
#37

Цитата:
без макроса в моем варианте номер не выдернуть?

Если не в режиме реалтайм (не в момент ответа), то вполне можете узнать используя переменную MEMBERINTERFACE но только после активации и перезагрузке очереди
setinterfacevar=yes в описании очереди, а не
Цитата:
setinterfacevar=yae
#38

спасибо! исправил на setinterfacevar=yes Embarassed
но все равно не в реальном времени не после разговора не отображает

пробовал:
exten => 3652,9,StopMixMonitor
exten => 3652,10,NoOp(${CUT(MEMBERINTERFACE,/,2)})
и
exten => 3652,1,Set(fname=${CALLERID(num)}--${CUT(MEMBERINTERFACE,/,2)}--${STRFTIME(${EPOCH},,%d.%m.%Y_%H-%M-%S)})-$
#39

Версия астера старенькая, к сожалению. А в 1.8 есть чудесная команда Dumpchan. Стоит ее вызвать в макросе, который срабатывает в момент поднятия трубки оператором = там ТАКИЕ интересные переменные есть!
_________________
P4 3.0 + 1Gb CentOS 5.8 Aster 1.8.16
Не люблю gui-сборки: натуральный продукт вкуснее.
И еще: я ПРОФИ так как НЕ ЛЕНЮСЬ читать литературу.