Conference rooms based on Asterisk
Foreword
Good day.
Two things inspired me to write this article: a small number or the lack of modern working examples on Asterisk "chips", as well as the reluctance of experts to share these same "chips" with the rest. This is me now about the RU-community. Any "grandfathers" on the forums would rather shower you with mud and send you to read books a decade old, than give you a little bit of useful information. The themes of the forums created in 2005-2010 are very outdated and sometimes something has already been cut out from the current version of the asterisk, and something needs to be redone very much to make it work.
So here.
As a result of the refusal of CUCM in favor of Asterisk, the management was tasked with keeping the most popular services among users in the most original state in order not to disturb people. One of these was the creation of conferences. By that time I was already familiar with Asterisk, but not so deeply, so it took me about a week and a half to go through all sorts of conference options, and a different task pushed me to the final decision.
The problem is that, from a solution that is similar to a solution, there is an article with an outdated meetme, as well as some kind of monster that I never managed to get to work with. I suggest something not so cumbersome.
Pulp
Describe what confbridge is, what sections in a particular config are responsible for, and what kind of option it is, I will not, this information is available and relevant. Now about the decision as a whole.
Task: to make it so that a conference can be created during a conversation, and then invite more subscribers there. The main problem is that the channelredirect function does not work as I would like. That is, if you execute it from the dialplan during a call, then one of the channels will fly away to the right place, and the second one will collapse, and climbing around the entire dialplan for 2k lines and setting the g option on Dials is lazy. And I don’t understand at all why in most manuals everyone is trying to solve the task only through dialplan, ignoring the possibility of the asterisk to work with external scripts and ami.
So. Asterisk 14.4.0
The conference script for 2 options (with comments):
conference.php
<?php//Готовим коннект к астеру
$host = "192.168.1.1";
$port = "5038";
$timeout = "10";
$user = "conference";
$pass = "1111";
//для того, чтобы не разбивать 1 задачу на 2 скрипта
$kusok = $argv[1];
//Кусок для создания конференции во время разговораif ($kusok == 1){
//Получаем переменные
$channel = $argv[2];
$bridgepeer = $argv[3];
$confnum = $argv[4];
print_r($bridgepeer);
print_r($confnum);
//Коннект
$sconn = fsockopen ($host, $port, $timeout) ordie ("Connection to $host:$port failed!");
fputs ($sconn, "Action: Login\r\n");
fputs ($sconn, "Username: $user\r\n");
fputs ($sconn, "Secret: $pass\r\n\r\n");
//Задаем переменные канала
fputs ($sconn, "Action: Setvar\r\n");
fputs ($sconn, "Channel: $channel\r\n");
fputs ($sconn, "Variable: CONFNUM\r\n");
fputs ($sconn, "Value: $confnum\r\n\r\n");
fputs ($sconn, "Action: Setvar\r\n");
fputs ($sconn, "Channel: $bridgepeer\r\n");
fputs ($sconn, "Variable: CONFNUM\r\n");
fputs ($sconn, "Value: $confnum\r\n\r\n");
//Редиректим
fputs ($sconn, "Action: Redirect\r\n");
fputs ($sconn, "Channel: $bridgepeer\r\n");
fputs ($sconn, "ExtraChannel: $channel\r\n");
fputs ($sconn, "Context: service_code-ael\r\n");
fputs ($sconn, "Exten: conference\r\n");
fputs ($sconn, "Priority: 1\r\n\r\n");
fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);
}
//Кусок для добавления нового участникаif ($kusok == 2) {
//Получаем переменные
$confnum = $argv[2];
$inviten = $argv[3];
$sconn = fsockopen ($host, $port, $errno, $errstr, $timeout) ordie ("Connection to $host:$port failed!");
//Подключаемся
fputs ($sconn, "Action: Login\r\n");
fputs ($sconn, "Username: $user\r\n");
fputs ($sconn, "Secret: $pass\r\n\r\n");
//Звоним и закидываем в конфу
fputs ($sconn, "Action: Originate\r\n");
fputs ($sconn, "Channel: Local/".$inviten."@out-ael\r\n");
fputs ($sconn, "Context: service_code-ael\r\n");
fputs ($sconn, "Exten: conference\r\n");
fputs ($sconn, "Priority: 1\r\n");
fputs ($sconn, "Variable: CONFNUM=".$confnum."\r\n\r\n");
fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);
}
}
Programming gurus can fix the code by making candy out of it, I wrote as best I could.
Next, we start using this script directly in Asterisk itself.
In order to create a conference, I chose the combination * 1. Briefly and does not intersect with the main numbering.
Add a script call to features.conf with passing the required variables to it
[applicationmap]
conference => *1,self,System(/usr/bin/php /home/script/conference.php 1 ${CHANNEL} ${BRIDGEPEER} ${CALLERID(num)})
Then, in order for this to work, we create a variable in the [globals] section of the dialplan and add our feature
DYNAMIC_FEATURES=conference
To add new participants to the already created conference, you will need to register the code in confbridge.conf
[default_menu]
type= menu
*1=dialplan_exec(service_code-ael,conference_add,1)
And now the most delicious, extensions.ael:
To create a conference (the php script addresses both conversational channels here):
conference => {
ConfBridge(${CONFNUM},,,default_menu);
}
To add a new user (here addresses dialplan_exec):
conference_add => {
Read(INVITEN,dial,11,i);
System(/usr/bin/php /home/script/conference.php 2 ${CALLERID(num)} ${INVITEN});
}
Everything. No kilotons of code in the dialplan. Everything is capacious. * 1 in the conversation and you are in the conference, once again * 1 beep and dialing, whom to add.
Galls
Driven by the wishes of users, I began to develop this feature.
The next was the ability to create conferences from scratch (not from a conversation), as well as enter the already created conferences by their number, and not wait for an inviting call.
Add to the dialplan:
_*1XXXX => {
NoOp(${CONFCHAN});
Set(__CONFNUM=${EXTEN:2});
System(/usr/bin/php /home/script/conference.php 3 ${CONFCHAN} ${CONFNUM} );
}
Add to script:
conference.php
//Для создание конференций с нуляif ($kusok == 3){
//Получаем переменные
$channel = $argv[2];
$confnum = $argv[3];
//Коннект
$sconn = fsockopen ($host, $port, $timeout) ordie ("Connection to $host:$port failed!");
fputs ($sconn, "Action: Login\r\n");
fputs ($sconn, "Username: $user\r\n");
fputs ($sconn, "Secret: $pass\r\n\r\n");
//Задаем переменные канала
fputs ($sconn, "Action: Setvar\r\n");
fputs ($sconn, "Channel: $channel\r\n");
fputs ($sconn, "Variable: CONFNUM\r\n");
fputs ($sconn, "Value: $confnum\r\n\r\n");
//Редиректим
fputs ($sconn, "Action: Redirect\r\n");
fputs ($sconn, "Channel: $channel\r\n");
fputs ($sconn, "Context: service_code-ael\r\n");
fputs ($sconn, "Exten: conference\r\n");
fputs ($sconn, "Priority: 1\r\n\r\n");
fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);
I also had to modify the _ * X line.
_*X. => {
set(__CONFCHAN=${CHANNEL});
Dial(Local/${EXTEN}@service_code-ael);
Now to enter the conference or create it from scratch, simply make a call to * 1 and a number, for example * 15234.
The final mutation of this service is the so-called “group conference”. This is when big bosses are too lazy to add everything manually, but I want to press one button and everything in the collection. For this, I decided to make separate service codes (* XXX) in order for people not to be confused. For our organization, it is unlikely that over1000 conference groups will be required in the next 100 years, so the numbering reserve should suffice. At home, you can add another prefix, so allocate another numbering capacity.
Dialplan:
_*XXX=> {
Set(CONFNUM=${CALLERID(num)});
System(/usr/bin/php /home/script/groups.php ${EXTEN:1} ${CONFNUM});
ConfBridge(${CONFNUM},,,default_menu);
}
The squeak of the collection of participants:
groups.php
//Функция звонкаfunctioncall($group, $confnum){
$many = count($group);
//Цикл разбора массива номеров группы на номераfor ($i=0; $many>$i; $i++) {
//достаем из массива номера
$num = trim(array_shift($group[$i]));
//Звоним
system("asterisk -rx \"channel originate Local/$num@out-ael application ConfBridge $confnum\"");
}
}
//функция получения массива группыfunctionconf_group($groupid){
//Коннектим в базу
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$pdo = new PDO("odbc:mssql_asterisk, "asterisk, "121212", $opt);
//Селектим из базы
$sql = "SELECT extension FROM [asterisk].[dbo].[conf_groups] where groupid = $groupid";
$select = $pdo->query($sql);
$result = $select->fetchAll();
//Дисконенктим
$pdo = NULL;
return $result;
}
//Получаем значение группы, которой надо позвонить
$groupid = $argv[1];
//Получаем номер конференции
$confnum = $argv[2];
//Получаем массив номеров
$group=conf_group($groupid);
//Звоним
call($group, $confnum);
}
All groups are stored in a database according to the structure “Groups, Number, Name, Description”. If a new group appears, simply add it to the database.
Now, to collect, for example, all the directors for a meeting, the general just needs to dial * 100. And as a rule, big bosses have big phones. Hence the bindim * 100 on any speeddial key, we sign as “director” and the user doesn't bother at all what to type. Button pressed - the meeting gathered.
Now anticipating your questions:
Why scripts and ami? Because I did not manage to make a sane redirect of both channels using the dialplan tools without losing them along the way. In ami, in the redirect function, you can attach an additional channel + set a variable to it (for example, a conference number, so that it can also add someone to it).
Also, you may have noticed that I rendered features in a separate context service_code-ael. This is convenient when all sorts of features you have more than a couple of pieces. I decided to do them through *, therefore in any context I simply write _ * X. and address in this context. Perhaps someone will find a more elegant solution, but I haven’t found it in a few months. And this functionality came to users like.
Why ael, but not conf? Well, because it is more structured and easier to read
. One gotoif function is worth something. To lua, I have not yet matured.
Why in the example of a massive collection originate made through bash, and not through AMI? The problem is that performing a bunch of originate in a row through ami, the system waits until the previous one is completed to give the next one. And if no one picks up the phone, and there 20 seconds no_ans and such 5 pieces? It will be possible to wait until the evening of the gathering.
Well, perhaps that's all. I hope this article will help the same seekers as I was, when it all needed to be done quickly, comfortably for users, and most importantly, in the future it was convenient to maintain this system myself, so to speak, with the future.