Введение
Ко мне обратились пользователи нашей сети с просьбой о блокировке уж слишком назойливой телефонной рекламы и выдали список номеров, которые требуется блокировать. Похожие вопросы периодически задают слушатели во время курсов. Естественно встал вопрос: что делать?
Поразмыслив над ситуацией я пришел к выводу, что нужно учесть следующее:
1. Номеров, которые подлежат блокировке, может быть много. Изменения в списке не должно приводить к изменениям в системе.
2. Попытки позвонить с запрещенных номеров нужно где-то фиксировать.
3. Решение не должно приводить к глобальным изменениям в плане вызовов и/или конфигурации системы.
В качестве платформы у меня имеется Asterisk без каких бы то ни было веб интерфейсов и PostgreSQL для записи детализации разговоров.
Что было решено сделать
1. Создать в PostgreSQL отдельную базу данных для работы с блокировкой. В базе данных создать две таблицы:
- Список заблокированных номеров.
- Журнал для фиксации попыток позвонить.
3. Немного подкорректировать план вызовов для запуска AGI сценария.
Создаем базу данных и таблицы в ней:
psql> create database asteriskban;
psql> \c asteriskban
pqsl> CREATE TABLE banned_numbers( banid serial PRIMARY KEY, banned_num VARCHAR (50) UNIQUE NOT NULL, who_add VARCHAR (50) NOT NULL, when_add TIMESTAMP NOT NULL );
pqsl> CREATE TABLE IF NOT EXISTS ban_log ( logid serial PRIMARY KEY, callerid VARCHAR (50) NOT NULL, dnis VARCHAR (50) NOT NULL, date TIMESTAMP NOT NULL );
psql> INSERT INTO banned_numbers (banned_num,who_add,when_add) VALUES ( '79876543210' , 'jdoe', NOW());
Далее создаем сценарий AGI. Этот скрипт следует разместить в каталоге /var/lib/asterisk/agi-bin/. Для меня Perl наиболее удобный язык сценариев, но вы можете использовать и другие python, php, bash, C, … Ниже содержимое этого сценария с пояснениями.
> cat /var/lib/asterisk/agi-bin/checkban.pl
#!/usr/bin/perl
$|=1;
#Поскольку используется БД, то на ряду с Asterisk::AG подключаем модуль DBI.
use DBI;
use Asterisk::AGI;
my $AGI = new Asterisk::AGI;
#После запуска AGI сценария Asterisk через STDOUT/STDIN передает в сценарий переменные канала. Мы это записываем в массив %input
my %input = $AGI->ReadParse();
#Готовим и запускаем SQL запрос, в качестве параметра запроса используется номер телефона, который передается как первый аргумент сценария.
my $dbh = DBI->connect('dbi:Pg:dbname=asteriskban;host=127.0.0.1','psqluser','password',{AutoCommit=>1,RaiseError=>1,PrintError=>0});
$sth = $dbh->prepare("SELECT banned_num from banned_numbers WHERE banned_num like ?");
$sth->execute( $ARGV[0] );
#Анализируем возврат запроса. Если запрос вернул данные (заблокированный номер), то звонок переводится в специальный контекст в диал плане: dialout. И в БД вносится запись о попытке запрещенного звонка. Если запрос данные не вернул, то просто завершаем работу сценария. $AGI->verbose добавлено, чтобы повысить информативность отладки.
if ( @row = $sth->fetchrow_array ) {
$AGI->verbose("Call from $row[0] banned",1);
my $sthi = $dbh->prepare("INSERT INTO ban_log (callerid, dnis, date) VALUES (?,?,NOW())");
$sthi->execute( $input{callerid},$input{dnid} );
$sthi->finish;
$AGI->set_context(dialout);
exit 0;
} else {
exit 0;
}
Легко заметить, что большая часть кода сценария - это работа с БД. Именно для этого я и использовал AGI. То что влияет на обработку вызова заключено всего в одной строке $AGI-> set_context(dialout); Остальное обертка вокруг этого.
Далее остается внести коррективы в extensions.conf
Первое создаем подпрограмму. Через эту подпрограмму будет вызываться переход на проверку номера.
[BannCheck]
exten=>_X.,1,Noop(Check if ${CALLERID(num)} in banned number)
same =>n,AGI(checkban.pl,${CALLERID(num)})
same =>n,return
В контекстах, для которых нужна такая проверка добавляем строку:
same =>n,gosub(BannCheck,${EXTEN},1)
Конечно, в выше описанном примере, можно обойтись и без подпрограммы, но подпрограмма может быть удобна в будущем. Так можно будет выполнить еще какие-нибудь действия перед или после проверки номера, и при этом не надо будет изменения вносить в нескольких местах.
И наконец контекст, в котором происходит завершение блокированного вызова. Здесь мы отвечаем на вызов, в CDR делаем пометку о том, что вызов был помечен как Blocked. И проигрываем файл с фразой «Ошибочный номер, попробуйте еще раз» (мой маленький троллинг вредных спамеров). После «кладем трубку».
[dialout]
exten=>_X.,1,Noop(Dial out)
same=>n,Answer()
same =>n,Set(CHANNEL(accountcode)=Blocked)
same =>n,Playback(invalid)
same =>n,Hangup()
Основная идея этой статьи показать, что с помощью AGI функционал вашего Asterisk может быть дополнен чем угодно. Фактически все ограничено только вашей фантазией и навыками написания сценариев.
Подробности работы с Asterisk и PostgreSQL вы можете изучить на курсах Asterisk базовый и PostgreSQL: Уровень 1. Основы SQL
Подпишись на рассылку актуальных новостей
и читай нас в соц. сетях