Екатерина
11.06.2019
6774

Скрипт включения 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 должен быть создан предварительно.

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

 
avatar
  Подписаться  
Уведомление о

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

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

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

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

ближайшие Вебинары

ONLINE

Why Choose HUGE?

Unlimited pre-designed elements

Each and every design element is designed for retina ready display on all kind of devices

User friendly interface and design

Each and every design element is designed for retina ready display on all kind of devices

100% editable layered PSD files

Each and every design element is designed for retina ready display on all kind of devices

Created using shape layers

Each and every design element is designed for retina ready display on all kind of devices