Екатерина
11.06.2019
9163

Скрипт включения DND путём имитации вызова сервисного кода *78

В этой статье рассмотрим реализацию скрипта, который будет имитировать нажатие сервисного кода *78 на аппарате (тем самым выставляя его в режим DND). Также рассмотрим необходимый кастомный диалплан под данный функционал. Кроме того изменим хинты (добьёмся того, чтобы статус DND корректно отображался в очередях и BLF). Перейдём к реализации. Для начала необходимо подготовить кастомный диалплан, который […]

В этой статье рассмотрим реализацию скрипта, который будет имитировать нажатие сервисного кода *78 на аппарате (тем самым выставляя его в режим DND). Также рассмотрим необходимый кастомный диалплан под данный функционал. Кроме того изменим хинты (добьёмся того, чтобы статус DND корректно отображался в очередях и BLF).

Перейдём к реализации. Для начала необходимо подготовить кастомный диалплан, который будет отвечать за постановку определённого номера в режим DND.

Кастомный диалплан.

Кастомный диалплан будет располагаться в файле extensions_custom.conf. Откроем его в режиме редактирования.

В контекст [from-internal-custom] поместим следующий диалплан:

exten => _*78X.,1,NoOp(==== ${DEVICE_STATE(SIP/${EXTEN:3})} ====)
same => n,GotoIf($["${DEVICE_STATE(SIP/${EXTEN:3})}" != "INVALID"]?app-dnd-on-put,${EXTEN:3},1)
exten => _*79X.,1,NoOp(==== ${DEVICE_STATE(SIP/${EXTEN:3})} ====)
same => n,GotoIf($["${DEVICE_STATE(SIP/${EXTEN:3})}" != "INVALID"]?app-dnd-off-put,${EXTEN:3},1)

Тем самым при наборе сервисного кода *78Внутренний_Номер_Абонента будет вызываться контекст app-dnd-on-put (опишем его ниже). В нём будет описан диалплан включения DND на экстеншене.

С app-dnd-off-put всё аналогично, только диалплан вызывается для снятия DND.

Данным диалпланом достигнем возможности ставить не только свой внутренний номер в DND, но также внутренний номер другого абонента.

Кроме того, если необходимо считать какую-либо статистику нахождения абонентов в DND, то данный скрипт можно добавить к выполнению текущего диалплана.

Приведём диалплан контекста app-dnd-on-put:

[app-dnd-on-put]
exten => _X.,1,Answer
exten => _X.,n,Wait(1)
exten => _X.,n,Macro(user-callerid,)
exten => _X.,n,Set(DB(DND/${EXTEN})=YES)
exten => _X.,n,Set(STATE=BUSY)
exten => _X.,n,Gosub(app-dnd-on-put,sstate,1())
exten => _X.,n(hook_1),Macro(hangupcall,)
exten => sstate,1,Set(DEVICE_STATE(Custom:DND${EXTEN})=${STATE})
exten => sstate,n,Set(DEVICES=${DB(AMPUSER/${EXTEN}/device)})
exten => sstate,n,GotoIf($["${DEVICES}" = "" ]?return)
exten => sstate,n,Set(LOOPCNT=${FIELDQTY(DEVICES,&)})
exten => sstate,n,Set(ITER=1)
exten => sstate,n(begin),Set(DEVICE_STATE(Custom:DEVDND${CUT(DEVICES,&,${ITER})})=${STATE})
exten => sstate,n,Set(ITER=$[${ITER} + 1])
exten => sstate,n,GotoIf($[${ITER} <= ${LOOPCNT}]?begin)
exten => sstate,n(return),Return()

Теперь рассмотрим контекст app-dnd-off-put:

[app-dnd-off-put]
exten => _X.,1,Answer
exten => _X.,n,Wait(1)
exten => _X.,n,Macro(user-callerid,)
exten => _X.,n,Noop(Deleting: DND/${EXTEN} ${DB_DELETE(DND/${EXTEN})})
exten => _X.,n,Set(STATE=NOT_INUSE)
exten => _X.,n,Gosub(app-dnd-off-put,sstate,1())
exten => _X.,n(hook_1),Macro(hangupcall,)
exten => sstate,1,Set(DEVICE_STATE(Custom:DND${EXTEN})=${STATE})
exten => sstate,n,Set(DEVICES=${DB(AMPUSER/${EXTEN}/device)})
exten => sstate,n,GotoIf($["${DEVICES}" = "" ]?return)
exten => sstate,n,Set(LOOPCNT=${FIELDQTY(DEVICES,&)})
exten => sstate,n,Set(ITER=1)
exten => sstate,n(begin),Set(DEVICE_STATE(Custom:DEVDND${CUT(DEVICES,&,${ITER})})=${STATE})
exten => sstate,n,Set(ITER=$[${ITER} + 1])
exten => sstate,n,GotoIf($[${ITER} <= ${LOOPCNT}]?begin)
exten => sstate,n(return),Return()

В итоге должен получиться диалплан, как на скриншоте ниже:

Диалплан включения/выключения DND

Сохраняем файл.

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

Первым делом зайдём в консоль Asterisk и выполним команду:

dialplan reload

Теперь с любого телефона или софтфона, подключённого к Asterisk’у необходимо набрать сервисный код *78X, где X — внутренний номер абонента, которого хотим поставить в DND.

К примеру, для того, чтобы поставить в режим DND телефон, с внутренним номером 0008, необходимо позвонить на сервисный код *780008.

Проверим, что сервисный код отработал и DND успешно установлен. Для этого есть несколько способов (к примеру можно позвонить на данный внутренний номер, если звонок не прошёл — значит DND успешно установлен). Но мы рассмотрим несколько иной способ — проверку через внутреннюю базу Asterisk — AstDB.

Выполним следующую команду в консоли Asterisk:

database get DND 0008

Если результат будет таким же, как показано на скриншоте ниже, значит диалплан отработал верно.

Проверка статуса DND

Этой же командой можно проверить, что сервисный код снятия DND (то есть *79Х) также отработает. Выведем телефон из режима DND и перейдём к написанию скрипта вызова данных сервисных кодов.

Скрипт вызова сервисных кодов

Основной принцип работы данного скрипта заключается в следующем: В качестве GET-запроса на вход скрипта подаются два параметра: внутренний номер телефона (на нём и будет включаться DND) и команда на включение/отключение DND. В процессе выполнения скрипт будет вызывать подготовленные коды снятия/постановки DND (описанные в предыдущем пункте).

Первым делом необходимо считать значение, которое передаётся GET-запросом. Ипользуем для этого тернарный оператор. В случае, если переменная account не задана в запросе, то $ext остаётся пустым.

$ext = (isset($_GET['account'])) ? trim($_GET['account']) : "";

Производить какие-либо действия с DND имеет смысл, только если переменная $ext не пуста, поэтому добавляем условие:

if ($ext != "") {

В данный блок помещаем следующий код:

writelog("extensions $ext found!");
if ($_GET['dnd'] == "on") {
	writelog("Enable DND on $ext");
	Call("*78".$ext);
	$ret = `/usr/sbin/asterisk -rx 'devstate change Custom:DEVDND{$ext} BUSY'`;
	writelog(trim($ret));
}
elseif ($_GET['dnd'] == "off") {
	writelog("Disable DND on $ext");
	Call("*79".$ext);
	$ret = `/usr/sbin/asterisk -rx 'devstate change Custom:DEVDND{$ext} NOT_INUSE'`;
		writelog(trim($ret));
}
else {
	writelog("ERROR: DND value is incorrect. Nothing to do.");
	exit();
}

Рассмотрим приведённый выше код. Сперва производится запись в лог соответствующей функцией writelog (будет рассмотрена немного позже). Затем проверяем, был ли передан GET-запросом параметр DND. В зависимости от его результата либо вызываем функцию Call с параметром *78 и номер абонента, либо *79 и номер абонента. В случае, если в качестве параметра DND ничего не передалось, либо передалось неверное значение, то пишем в лог ошибку и выходим из программы.

На этом закрывается условие $ext != “”, т.е. в код дописываем:

}
else {
	writelog("ERROR: Not found extensions. Nothing to do");
}

Тем самым в скрипт добавили обработчик ситуации, если внутренний номер не был передан.

Теперь рассмотрим функцию, предназначенную для вызова сервисного кода. Производиться вызов будет с помощью AMI функции Originate.

В данной функции обязательно указать 3 переменные:

$port — порт подключения к AMI;

$username — имя пользователя AMI;

$password — пароль подключения к AMI для данного пользователя.

Все эти данные можно найти в файле manager.conf

На скриншоте ниже показан пример такого файла. При этом значения переменных распределятся следующим образом: $port = 5038; $username = admin; $password = nasdnioh1123nasd8h12jbqd8y21e1bdq2.

Пример файла manager.conf

Функция Call в данном случае имеет весьма стандартную структуру, так что описывать подробно её не будем, лишь приведём код:

function Call($numb) {
	$port = 5038;
	$username = "admin";
	$password = "nasdnioh1123nasd8h12jbqd8y21e1bdq2";
	$context = "from-internal";
	$socket = stream_socket_client("tcp://127.0.0.1:$port");
	if($socket) {
		writelog("Connected to socket, sending authentication request.");
		// Prepare authentication request
		$authenticationRequest = "Action: Login\r\n";
		$authenticationRequest .= "Username: $username\r\n";
		$authenticationRequest .= "Secret: $password\r\n";
		$authenticationRequest .= "Events: off\r\n\r\n";
		// Send authentication request
		$authenticate = stream_socket_sendto($socket, $authenticationRequest);
		if($authenticate > 0) {
			// Wait for server response
			usleep(200000);
			// Read server response
			$authenticateResponse = fread($socket, 4096);
			// Check if authentication was successful
			if(strpos($authenticateResponse, 'Success') !== false) {
				writelog("Authenticated to Asterisk Manager Inteface. Initiating call.");
				// Prepare originate request
				$originateRequest = "Action: Originate\r\n";
				$originateRequest .= "Channel: Local/$numb@$context/n\r\n";
				$originateRequest .= "Async: yes\r\n\r\n";
				// Send originate request
				$originate = stream_socket_sendto($socket, $originateRequest);
				if($originate > 0) {
					// Wait for server response
					usleep(200000);
					// Read server response
					$originateResponse = fread($socket, 4096);
					// Check if originate was successful
					if(strpos($originateResponse, 'Success') !== false) {
						writelog("Call initiated, dialing.");
					} else {
						writelog("Could not initiate call.");
					}
				} else {
					writelog("Could not write call initiation request to socket.");
				}
			} else {
				writelog("Could not authenticate to Asterisk Manager Interface.");
			}
		} else {
			writelog("Could not write authentication request to socket.");
		}
	} else {
		writelog("ERROR: Unable to connect to socket.");
	}
}

Одно из существенных изменений, которое было внесено в данный скрипт — это внедрение своей функции логирования (writelog). Таким образом, весь лог скрипта собирается и записывается в отдельный файл.

Остаётся рассмотреть последнюю и самую короткую функцию — writelog. На вход она принимает строку, которая будет выведена в файле лога (dndlog)

function writelog($str) {
	$fl = fopen("/var/log/asterisk/dndlog", "a");
	fwrite($fl, date('Y-m-d H:i:s')."  ".$str."\n");
	fclose($fl);
}
Для правильной работы скрипта, файл dndlog должен быть создан предварительно.

На этом описание файла скрипта закончено. Можно сохранять его и проверять работу.

Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Остались вопросы?

Я - Компаниец Никита, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.

Безопасен в использовании.

Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.

Надежен в эксплуатации.

Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.

Гибкий в настройке.

Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.