Пользователь
0,0
рейтинг
14 ноября 2014 в 15:51

Разработка → Asterisk+Lua — IVR и некоторые другие моменты

И снова здрасьте! IVR — сегодня это даже не фишка, а некий стандарт работы предприятия. В некоторых случаях многие, как клиенты, так и конкуренты считают, что если этого самого IVR нет, то «там» низкое качество предоставляемых услуг. Этой штукой нынче никого не удивишь. Однако, мы же говорим про реализацию IVR на языке lua под Астериском. И если вы переходите от обычного плана набора к Lua, то тут можно кое-что пояснить.


Предположим, что нужные файлы для меню у вас уже есть и они лежат в нужной папке. Возможно, что на старом конфиге они даже использовались. Тогда, при описании голосового меню на Lua делаем (я делал) так:

Описываем где-то в начале файла табличку с файлами. Всё удобнее в одном месте при необходимости менять:

mhold = {
    m_hello = "custom/message_01";        -- Приветствие
    m_menu = "custom/message_02";       -- описание меню
    m_thx = "custom/message_05";           -- Спасибо за выбор нашей компании...
    good_day = "custom/wav_gd_2";         -- типа "Хорошего вам дня"...
    comerc = "custom/com_1";                  -- Нажмите для того-то 1
    live = "custom/live_2";                          -- ну и т.д. другие варианты выбора и нажатий.
    other = "custom/other_3";
}


Таким образом вы описали в табличке поля с указанием применяемых файлов. В моём случае — файлы с приветствием, какие-то благодарственные слова и выборка меню. Так же был резервный (на всякий случай) вариант, при котором каждое меню было разбито на свой файл…

Далее, в первой статье я в описании входящих звонков, в функции foo() указал вызов ivr(). Так вот, фактически в моём примере это работало так:
Событие — Входящий звонок. Вызов foo(). Проверка некоторых условий -> проигрывание приветствия -> вызов ivr(). Само приветствие я проигрывал до вызова ivr().

function ivr(d)
    app.noop("Включено голосовое меню.")
    app.noop("DID: "..d)
    app.background(mhold.m_menu,"","","menu")
    app.waitexten(3)
    return
end


Не такая уж и навороченная функция. app.background тут и есть вызов из ядра астериска голосового меню. Но при этом нужно так же сделать описание события menu в экстеншинах:

menu = {
	["1"] = function(c,e)
	    app.noop("Calling from menu by 1")
	    app.playback(mhold.m_thx)
	    CallSKS()                                 -- на обработчик вызова СКС
	end;
	["2"] = function(c,e)
	    app.noop("Calling from menu by 2")
	    app.playback(mhold.m_thx)
	    app.goto("local_ext","4690",1)   -- тут и далее я вызываю абонентов сразу группами.
	end;
	["3"] = function(c,e)
	    app.noop("Calling from menu by 3")
	    app.playback(mhold.m_thx)
	    app.goto("local_ext","4579",1)
	end;
	["4"] = function(c,e)
	    app.noop("Calling from menu by 4")
	    app.playback(mhold.m_thx)
	    CallSKS()                                   -- тоже вариант обращения в клиентскую службу.
	end;
    };


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

from_trunk = {
        t = function()
                app.playback(mhold.m_thx)
                app.goto("local_ext","7090",1)
        end;
-- далее остальная часть описания событий...


Про IVR кажется всё. Теперь ещё один момент: ограничение времени звонка. Да, к сожалению мне и такое приходилось делать. Были отдельные кадры в компании, которые любили по межгороду просто десятками минут, а за месяц — часами болтать с друзьями, подругами, братьями и т.д. Конечно, таких наказывали рублём, но позиция руководства была такой, что на работе надо работать. Попросили ограничить болтунов во времени. В нижеследующем примере будет показано, как можно сделать это глобально, с выборкой направления (хотя возможно не самый лучший вариант).

-- где-то внутри функции trunk_test
if string.len(num) > 7 and not string.find(string.sub(num,1,4),"8383",1) then
        app.noop("Действует ограничение на длительность звонка 10 минут!!!")
        app.dial(string.format("%s%s,60,tTL(600000:480000:60000)",trunk.startel,num))
else
        app.noop("Звонок по городу.")
        app.dial(string.format("%s%s,60,tT",trunk.startel,num))
end


В данном случае я сделал выборку по количеству символов в набираемом номере. Если количество символов превышает количество символов нашего города, исключая код нашего города, тогда это звонок по межгороду. Сотовые местные обрабатываются отдельно, поэтому под это правило не попадают (в прошлой статье про DEF-коды). А вот сотовые межгорода — попали. В моём примере действует ограничение на 10минут. за 7 (или 8?) минут абонент слышит лёгкий beep. В данной функции можно добавить обработку только конкретных «злодеев». Можно этих злодеев
занести в таблицу и по ней уже смотреть, а можно в базу mysql и данные от туда вытаскивать, кому как удобно…

Всё. Всем до свиданья!
@Sayman_nsk
карма
6,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (9)

  • 0
    Зачем вы используете Background/Playback/Waitexten?
    Есть же отличный Read и конструкцию if else. Гораздо гибче. Попробуйте.
    • –1
      Playback тут для проигрывания. Read же не умеет в линию музыку выдавать, верно, а Playback умеет. А про waitexten, тут вы правы. Но тут главное же показать для начала более простые вариации, для тех кто не знает или плохо знает.
      За подсказку спасибо.
      • 0
        почитайте документацию прежде чем отвечать на вопрос…
        • 0
          «Ближе к телу». Read никогда не пользовался, в документацию по неё смотрел только мельком. Waitexten и playback используются по назначению. Что не так?
          • 0
            Playback тут для проигрывания. Read же не умеет в линию музыку выдавать, верно, а Playback умеет

            Не так- что Вы написали о функции не зная возможностей функции. Не более. Я не критиковал использование функций вами, я критиковал то, что вы пред ответом не удосужились документацию прочитать… Да что там документацию- 2 сточки…
            • 0
              я с Read() пока не работал, по вашей наводке пробежался мельком. Но это не самое главное. мне больше интересно узнать. как можно перехватить в диалплане событие поднятия трубки. т.е. сделали dial(), абонент ответил и вот тут мне надо это событие определять.
              • 0
                Вам надо какие то действия в диалплане выполнить?
                Я это делаю через флаг U для Dial()
                К примеру вот хак когда для миксмонитора не работает опция b:

                app.Dial(SIP/DEVICE,TIMEOUT,TtU(mixmonitor^FILEPATH/CALLFILENAME^monopt))

                У меня есть контекст где обрабатывается mixmonior только после поднятия трубки.

                Более подробно:
                core show application Dial

                • 0
                  я запись включаю так:
                  function record(v1)
                      local monopt
                      local fname = string.format("%s-%s%s%s", v1, date.day, date.month, date.year)
                      WAV = "/var/log/asterisk/wav/"
                      MP3 = string.format("/records/%s/%s/%s/", date.year, date.month, date.day)
                      monopt = string.format("/bin/nice -n 19 /usr/bin/lame -b 16 --silent %s%s.wav %s%s.mp3 && rm -f %s%s.wav",WAV,fname,MP3,fname,WAV,fname)
                      app.mixmonitor(string.format("%s%s.wav,b v(3) V(3) W(3),%s",WAV,fname,monopt))
                      channel["CDR(recordingfile)"]:set(fname..".mp3")
                  end
                  

                  надо пронаблюдать про опцию b… но мне там не запись нужно, я хочу при поднятии трубки (с точки зрения ami
                  это будет событие bridge) сам сгенерить некоторое событие, в том числе на http отправить. проблема именно с выдёргиванием этого события. Флаг U описан как, execute via gosub the routine for the called channel before connecting. Т.е. перед соединением. мне нужно после (after connecting).
                  • 0
                    Это происходит непосредственно перед соединением. то есть когда поднимается трубка (прилетает 200 ответ если это SIP)? перед тем как обработать событие астериск начинает работу gosub. То есть непосредственно перед событием поднятия трубки.

                    В gosub можно сделать все что угодно вашей душе.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.