Получение номера, который ответил в очереди.
Идейно, я понимаю, что будет некоторый скриптик на ПХП крутиться и слушать происходящее не порту. Как это реализовать - для меня вообще не понятно.
Вопрос какого плана: с чего начинать копать в эту область? Есть ли какие-то примеры для новичков?
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
http://www.voip-info.org/wiki/view/Asterisk+log+queue_log
_________________
Gentoo Linux || Asterisk 11.2-cert3
Решения телефонии на базе Asterisk || http://it-need.ru
Пока я вижу реализацию так:
Скрипт с вечным циклом висит на порту менеджера и ждет пока от * не придет EOF. Потом разбирает строчку. Если это - AgentAnswer (или что-то такое), посылаем запрос на CallerID, соотв. этому каналу. Ждем ответ. При ответе - высылаем оба номера внешнему сервису.
Готов выслушать критику
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
А как организовано висение скрипта на порту? Или он работает в формате "подключил-спросил-отключился"?
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
в астериске в команде queue можно вызвать макрос или аги после поднятия трубки. вот там и делайте, насколько помню, скрипт срабатывает в момент поднятия агентом трубки, при этом соеденение еще не происходит, оно произойдет по завершению скрипта. тем не менее, в этот момент доступны все ресурсы, можно вытащить данные, кто ответил и сохранить в астдб например. не в канал, так как это временный канал, скрипта.
_________________
Gentoo Linux || Asterisk 11.2-cert3
Решения телефонии на базе Asterisk || http://it-need.ru
О.. Спасибо большое. Как-то не подумал про этот момент.
2 aven
К сожалению, я плохо понимаю, как по записи параметра в sql можно сделать внешний вызов чего-либо. Плюс, как я понимаю, лог записывается ПОСЛЕ того, как трубка будет положена (чтобы время разговора записать).
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
так что либо проапдейтится на что то более новое либо пилить AMI или логи очереди...
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, временами у астериска крыша едет...
я не вижу как в отгрепанном аги коде Вы получаете номер ответившего агента. Про то, что это можно сделать в AMI я не спорю.
| Код: |
| .... 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)
| Игорь писал(а): |
| в середине звонка,... |
не в середине, а в самый момент ответа
_________________
нанотехнолигии в области Asterisk
| Игорь писал(а): |
| 2 andyk74: это все замечательно, но эти переменные ставятся _после_ соединения, при выполнения макроса в очереди соедиенеия еще нет и переменные пустые. Помоему единственное, как макрос топикстартеру может помочь - вытащить CHANNEL и распарсить его, с некоторой долей вероятности этого автору хватит. я не вижу как в отгрепанном аги коде Вы получаете номер ответившего агента. Про то, что это можно сделать в AMI я не спорю. |
в adhearsion ами и аги имеют-таки точки соеденения, вот эта функция (connected_with_agent), как раз и вызывает метод, который на основе канала (ответившего) и основного канала, чарез АМИ устанавливает переменные. навороченно, но в моем случае оправданно, так как у меня много действий с менеджером, я вынужден следить за состоянием каналов. код могу дать.
_________________
Asterisk 1.4.30 @ Ubuntu 9.04 + Cisco MC3810 + NEC NEAX 2000IPS + Polycom IP Phones
_________________
e=mc^2
| 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
| Цитата: |
| в астериске в команде queue можно вызвать макрос или аги после поднятия трубки. вот там и делайте, насколько помню, скрипт срабатывает в момент поднятия агентом трубки, при этом соеденение еще не происходит, оно произойдет по завершению скрипта. тем не менее, в этот момент доступны все ресурсы, можно вытащить данные, кто ответил и сохранить в астдб например. не в канал, так как это временный канал, скрипта. |
Кстати почему-то данный макрос у меня не работал, помнится мне, что в него не так просто было передать переменные.
_________________
e=mc^2
Пошло событие 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"> |
Делаю так:
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-переменной.
Чудеса
_________________
P4 3.0 + 1Gb CentOS 5.8 Aster 1.8.16
Не люблю gui-сборки: натуральный продукт вкуснее.
И еще: я ПРОФИ так как НЕ ЛЕНЮСЬ читать литературу.
Вывести номер клиента удалось только с помощью ${CONNECTEDLINE(num)}
нужно, чтобы номер ответившего в группе отображался в записанном файле
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
_________________
платный суппорт по мере возможностей
exten => h,n,NoOp( == ${CUT(MEMBERINTERFACE,/,2)} == )
чтобы номер ответившего отобразился в имени файла
Added after 10 minutes:
я вот это ${CUT(MEMBERINTERFACE,/,2)} вставляю в разные места extension.conf, что то не реагирует никак
Согласно логике вещей его наверное надо вставить в формирование имени файла.
exstentions.conf
exten => 3652,1,Set(fname=${CALLERID(num)}--${CUT(MEMBERINTERFACE,/,2)}--${STRFTIME(${EPOCH},,%d.%m.%Y_%H-%M-%S)})-$
и в файле на это месте получается --
так то очереди работают без проблем
| Цитата: |
| без макроса в моем варианте номер не выдернуть? |
Если не в режиме реалтайм (не в момент ответа), то вполне можете узнать используя переменную MEMBERINTERFACE но только после активации и перезагрузке очереди
setinterfacevar=yes в описании очереди, а не
| Цитата: |
| setinterfacevar=yae |
но все равно не в реальном времени не после разговора не отображает
пробовал:
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)})-$
_________________
P4 3.0 + 1Gb CentOS 5.8 Aster 1.8.16
Не люблю gui-сборки: натуральный продукт вкуснее.
И еще: я ПРОФИ так как НЕ ЛЕНЮСЬ читать литературу.