Конференц-колл 17.05.2011

Для тех, кто в танке, конференц-колл – возможность разговора одновременно нескольких участников.

В документациях к астериск описывается приложение MeetMe (которое заведует конференциями) и способ организовать "статические залы" - т.е., мы заранее прописываем номер зала и пароль, а потом пользователи к нему присоединяются.

Недостаток этого, на мой взгляд, в следующем: допустим, я организовал конференцию в конференц-зале с номером 1 и разговариваю с поставщиком. Мой коллега решает тоже поговорить в режиме конференции и организует конференцию. Для начала ему нужно как-то узнать – свободен ли зал 1, и потом занять зал 2. Третий участник уже должен опрашивать двоих…

В общем, мне видится правильным другой путь:

  1. Я организую конференцию, звоня на служебный номер, например, *9*.
  2. Я набираю номер того, кого хочу присоединить (через 0).
  3. Человек присоединяется. Если он из внутренних пользователей, он так же может пригласить нового участника.

Начальная настройка.

Создаем файл /etc/asterisk/meetme.conf и вносим в него:

[general]
; Разрешаем использование realtime
schedule=yes
; таблица будет обновляться при входе/выходе участников
logmembercount=yes

Как и в предыдущих случаях, данные конференций будут храниться в MySQL. Создадим для них таблицу:

CREATE TABLE tbl_meetme (
  confno char(80) NOT NULL default '0',
  starttime datetime NOT NULL default '0000-00-00 00:00:00',
  endtime datetime NOT NULL default '2099-12-31 23:59:59' ,
  pin char(20) default NULL,
  opts char(100) default NULL,
  adminpin char(20) default NULL,
  adminopts char(100) default NULL,
  members int(11) NOT NULL default '0',
  maxusers int(11) NOT NULL default '0',
  created_at datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (confno,starttime)
);

И внесем строчку в /etc/asterisk/extconfig.conf

meetme => odbc,asterisk,tbl_meetme

Так же, восстановим из копии файл /etc/asterisk/indications.conf и в секции [general] установим country=ru.

Не забываем перезапустить asterisk.

Создание конференции

Для начала – займемся процедурой создания конференции. Задача звучит так: по набору *9* необходимо вставить строчку в таблицу и вернуть номер конференции, после чего задиалится на него.

Создадим хранимку в MySQL, которая создает новую конференцию и возвращает ее номер. Заодно, она подчищает конференции, созданные более одного дня назад.

DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `create_meetme_conference`()
BEGIN
    DELETE FROM tbl_meetme WHERE members=0 AND DATE_ADD(created_at, INTERVAL 1 DAY)<NOW();
    SELECT @NewConfNo:=IFNULL(CAST(MAX(confno) as UNSIGNED),0)+1
    FROM tbl_meetme;
    INSERT INTO tbl_meetme (confno,created_at) SELECT @NewConfNo,NOW();
    SELECT @NewConfNo;
END

В файл func_odbc.conf добавляем вызов этой хранимки

[CREATE_NEW_CONFERENCE]
; создание новой meetme конференции
dsn=asterisk
readsql=call asterisk.create_meetme_conference

В диалплан добавляем (пока – в тестовых целях) создание конференции

[dial_system_functions]

exten => *9*,1,NoOp(Start Conference)
exten => *9*,n,Goto(conference_main,s,1)
exten => *9*,n,Return

[conference_main]
; контекст работы с конференцией
exten => s,1,NoOp(conference_main)
; ставим запрет на динамические фишки
exten => s,n,Set(__DYNAMIC_FEATURES=)
; получаем номер конференции при помощи процедуры (если его еще нет)
exten => s,n,Set(__CONFNO=${IF($["${CONFNO}"=""]?${ODBC_CREATE_NEW_CONFERENCE()}:${CONFNO})})
; устанавливаем контекст, по которому будет производится выход (в нашем случае - доп. набор)
exten => s,n,Set(MEETME_EXIT_CONTEXT=conference_invite_user)
; проигрываем приветствие
exten => s,n,Playback(rittal/meetme_welcome)
; если это внутренний пользователь - рассказываем как приглашать
exten => s,n,GotoIf($["${SIPPEER(${CALLERID(num)},dynamic)}" = "yes"]?:create_conf)
exten => s,n,Playback(rittal/meetme_add_possible)
; и запускаем конференцию
exten => s,n(create_conf),MeetMe(${CONFNO},X1)

Приглашение в конференцию

Пользователь, уже находящийся в конференции, должен иметь возможность пригласить еще кого-то. Идея взята отсюда.

Сформулируем задачу так:

  1. Находясь в конференции, пользователь нажимает 0.
  2. Пользователь выпрыгивает из конференции в набор номера.
  3. После набора номера и разговора с набранным номером, если пользователь нажимает **, он подключает набранного человека к конференции, если *# - не подключает.
  4. Далее пользователь возвращается в конференцию обратно.

Для начала, создадим файл /etc/asterisk/features.conf и пропишем в него две комбинации - ** и *#.

[general]

[featuremap]

[applicationmap]
conference_invite_user => *0,caller,Macro,conference_invite_user
conference_invite_cancel => *#,caller,Macro,conference_invite_cancel
conference_invite_ok => **,caller,Macro,conference_invite_ok

Теперь диалплан приглашения и макросы реакции на комбинации

[conference_invite_user]
; контекст для приглашения пользователей в конференцию
exten => 0,1,NoOp(conference_invite_user. ConfNo: ${CONFNO})

; проверяем, что это внутренний номер - только им можно приглашать участников

exten => 0,n,GotoIf($["${SIPPEER(${CALLERID(num)},dynamic)}" = "yes"]?:return_to_conference)
exten => 0,n,Playback(rittal/meetme_add_instruction)

; читаем номер, набранный пользователем, попутно выдавая тон
exten => 0,n,Read(NEWDIAL,dial,,i)
; устанавливаем разрешения на работу функций по переводу звонка
exten => 0,n,Set(__DYNAMIC_FEATURES=conference_invite_ok#conference_invite_cancel)
; и набираем номер, устанавливая флаг "продолжить после хенгапа"
exten => 0,n,Gosub(internal_phones_outgoing_dial,${NEWDIAL},1)
; возвращаемся обратно в конференцию
exten => 0,n(return_to_conference),Goto(conference_main,s,1)

[macro-conference_invite_ok]
; если отвечено **,
exten => s,1,NoOp(macro-conference_invite_ok)
; то переводим пользователя в конференцию,
exten => s,n,ChannelRedirect(${BRIDGEPEER},conference_main,s,1)
; и сами возвращаемся туда.

[macro-conference_invite_cancel]
; если отвечено *#, то кладем трубку и возвращаемся в конференцию
exten => s,1,NoOp(macro-conference_invite_cancel)
exten => s,n,SoftHangup(${BRIDGEPEER})