По вашему запросу ничего не найдено :(
Убедитесь, что запрос написан правильно, или посмотрите другие наши статьи:
img
Уже прошло несколько лет с тех пор, как появился Kotlin, и он преуспевает. Так как Kotlin был создан в качестве замены Java, то его, как и следовало ожидать, во многом сравнивают именно с ним. Чтобы помочь вам выбрать один из двух языков, я сравню основные аспекты каждого из них.  Вот 8 пунктов, которые я рассмотрю в этой статье: Синтаксис Лямбда-выражения Обработка нулей Классы предметной области Глобальные переменные Параллелизм Функции расширения Сообщество Сравнение синтаксиса Давайте сначала проведем сравнение базового синтаксиса. Я думаю, что многие из вас, кто читает эту статью, возможно, уже знают кое-что о Java и/или Kotlin, но я все же приведу пример, чтобы мы могли явно их сравнить: Java public class HelloClass { public void FullName(String firstName, String lastName) {    String fullName = firstName + " " + lastName; System.out.println("My name is : " + fullName); }       public void Age() { int age = 21; System.out.println("My age is : " + age); } public static void main(String args[]) { HelloClass hello = new HelloClass(); hello.FullName("John","Doe");        hello.Age(); } } Kotlin class NameClass {    fun FullName(firstName: String, lastName:String) {        var fullName = "$firstName $lastName"        println("My Name is : $fullName")    } } fun Age() { var age : Int    age = 21    println("My age is: $age") } fun main(args: Array) {    NameClass().FullName("John","Doe")    Age() } Код не сильно отличается, если не считать небольших различий в синтаксисе методов и классов. Но реальное различие заключается в том, что Kotlin поддерживает вывод типов, где тип переменной не нужно объявлять. А еще там не нужны точки с запятой ( ; ). Также можно отметить, что Kotlin не сильно придерживается принципов ООП, в отличие от Java, где все содержится внутри класса. Посмотрите хотя бы на функции Age и main в примере – они не находятся внутри класса. Kotlin, как правило, имеет меньше строк кола, а вот Java придерживается традиционного подхода и, соответственно, является достаточно многословным.  Одно из преимуществ Kotlin перед Java – он может делать все, используя как традиционный ООП-подход, так и любой другой.  Лямбда-выражения Если мы заговорили о Java и Kotlin, то мы, конечно же, должны поговорить об их знаменитых лямбда-выражениях. У Kotlin есть встроенная поддержка лямбда-функций (и всегда была), а вот в Java она появилась только в Java 8.  Давайте посмотрим, как они выглядят. Java //syntaxes parameter -> expression (parameter1, parameter2) -> { code } //sample usage ArrayList numbers = new ArrayList(); numbers.add(5); numbers.add(9); numbers.forEach( (n) -> { System.out.println(n); } ); Kotlin //syntax { parameter1, parameter2 -> code } //sample usage max(strings, { a, b -> a.length < b.length }) В Java скобки не являются обязательными: если есть только один параметр, то они не нужны. Но в Kotlin скобки нужны всегда. Однако, в общем и целом, помимо синтаксиса, различий не так уж и много.  Как по мне, лямбда-функции используются только в качестве методов обратного вызова. Хоть они и имеют гораздо более широкий спектр применений, все же они не очень читабельны, в связи с чем их реже используют. Они, конечно, сократят объем вашего кода, но разобраться в нем потом будет достаточно сложно.  Это вопрос предпочтений, но мне кажется, что намного удобнее, когда скобки используются всегда, как в Kotlin, потому что так код становится более читабельным.  Обработка нулей В объектно-ориентированном языке значения нулевого типа всегда были проблемой. Когда вы пытаетесь использовать содержимое нулевого значения, выпадает исключение нулевого указателя (NPE - Null Pointer Exception). Поскольку NPE всегда были проблемой, то и у Java, и у Kotlin есть свой способ обработки нулевых объектов. Как они это делают, я покажу ниже.  Java Object object = objServ.getObject(); //traditional approach of null checking if(object!=null){    System.out.println(object.getValue()); } //Optional was introduced in Java 8 to further help with null values //Optional nullable will allow null object Optional objectOptional = Optional.ofNullable(objServ.getObject()); //Optional.of - throws NullPointerException if passed parameter is null Optional objectNotNull = Optional.of(anotherObj); if(objectOptional.isPresent()){    Object object = objectOptional.get();    System.out.println(object.getValue()); } System.out.println(objectNotNull.getValue()); Kotlin  //Kotlin uses null safety mechanism var a: String = "abc" // Regular initialization means non-null by default a = null // compilation error //allowing null only if it is set Nullable var b: String? = "abc" // can be set null b = null // ok print(b) Сколько себя помню, в Java использовалась обычная проверка нулей, которой свойственен человеческий фактор. Затем в Java 8 появились необязательные классы, которые создают возможность для более надежной проверки нулевых значений, особенно со стороны API/сервера.  А вот Kotlin, если переменная допускает возможность нулевого значения, позволяет использовать операторы безопасного вызова. Я еще не пользовался необязательным классом, но, мне кажется, что его механизм работы и назначение чем-то похожи на операторы безопасного вызова в Kotlin. Оба варианта помогают определить, какая переменная может быть нулевой, и убедиться, что была реализована корректная проверка.  Иногда в коде может быть много переменных, которые надо проверить, и их слишком много для того, чтобы проверить их все. Но если вы будете добавлять проверки везде, где нужно, это сделает нашу кодовую базу просто безобразной, а это, я думаю, никому не понравится, так ведь? На мой взгляд, использование необязательных классов Java ощущается более сложным из-за объема кода, который надо добавить. А вот в Kotlin можно просто добавить немного кода, который будет проверять нулевые значения за вас.  Класс предметной области Некоторые называют это классом-сущностью. Ниже я привел примеры того, как классы используются в качестве классов предметной области в каждом языке. Java public class Student {     private String name;     private Integer age;         // Default constructor public Student() { }     public void setName(String name) {         this.name = name;     }     public String getName() {         return name;     }         public void setAge(Integer age) {         this.age = age;     }     public Integer getAge() {         return age;     } } Kotlin //Kotlin data class data class Student(var name: String = "", var age: Int = 0) //Usage var student: Student = Student("John Doe", 21) В Java свойства объявляются как частные (private) в соответствии с технологией инкапсуляции. Когда вы пытаетесь получить доступ к этим свойствам, Java использует сеттеры и геттеры, а также методы isEqual или toString, когда это необходимо. В Kotlin классы данных вводятся для определенных целей классов предметной области. Классы данных обеспечивают прямой доступ к свойствам. Они также предоставляют несколько встроенных служебных методов, таких как equals(), toString() и copy(). Я считаю, что классы данных — одна из лучших вещей, которые предлагает Kotlin. Они стремятся уменьшить количество шаблонного кода, который требуется для обычных классов предметной области, и они хорошо справляются с этой задачей. Случайное фото… потому что вы уже на полпути! Глобальные переменные Иногда может возникнуть необходимость создать такую переменную, которая была бы доступна в вашей кодовой базе везде. Для этого существуют глобальные переменные. Kotlin и Java имеют свои способы решения этой проблемы.  Java public class SomeClass { public static int globalNumber = 10; } //can be called without initializing the class SomeClass.globalNumber; Kotlin class SomeClass {    companion object {        val globalNumber = 10    } } //called exactly the same like usual SomeClass.globalNumber Некоторые из вас, возможно, уже знакомы с ключевым словом static, которое используется в некоторых других языках, таких как C++. Оно инициализируется в начале выполнения программы, и Java использует его для создания глобальных переменных, так как оно не содержится в качестве объекта. Это значит, что к нему можно получить доступ откуда угодно без инициализации класса как объекта.  Kotlin использует здесь совершенно иной подход: он убирает ключевое слово static и заменяет его объектом-компаньоном, который очень похож на одноэлементный класс. Это позволяет реализовывать сложные функциональные средства, такие как расширения и интерфейсы. То, что в Kotlin отсутствует ключевое слово static, застало меня врасплох. Вы, конечно, можете возразить, что использование ключевого слова static не самый удачный подход из-за его природы и сложности его проверки, и что, конечно же, объект-компаньон в Kotlin может легко его заменить. Даже в таком случае использование static для глобальной переменной не должно быть слишком сложным. Если мы будем осторожны с глобальными переменными и не сделаем для себя привычкой создавать их постоянно, то у нас все будет в порядке. Объект-компаньон также может дать нам некоторую гибкость при взаимодействии и т.п., но как часто мы будем взаимодействовать с одноэлементными классами? Я думаю, что ключевые слова static помогают нам сохранять краткость и ясность в отношении глобальных переменных. Параллелизм В наши дни параллелизм является популярной темой для обсуждения. Иногда способность языка программирования выполнять несколько задач одновременно может стать решающим критерием при выборе языка.  Теперь давайте посмотрим, как эти два языка решают эту задачу. Java // Java code for thread creation by extending // the Thread class class MultithreadingDemo extends Thread {    public void run()    {        try        {            // Displaying the thread that is running            System.out.println ("Thread " +                  Thread.currentThread().getId() +                  " is running");        }        catch (Exception e)        {            // Throwing an exception            System.out.println ("Exception is caught");        }    } } // Main Class public class Multithread {    public static void main(String[] args)    {        int n = 8; // Number of threads        for (int i=0; i) {    var number = 1    var result = number.plusOne()    println("Result is: $result") } Они позволяют добавить в класс новую функцию, при этом она не будет распространяться на класс или не нужно будет использовать какие-то замысловатые шаблоны проектирования. Она даже позволяет вам добавить функцию к изменяемому классу Kotlin. Вы практически можете попрощаться с тем самым методом lib, который требует, чтобы вы передавали все внутри своих параметров. Сообщество И последнее, но оттого не менее важное: давайте поговорим о чем-то нетехнического характера. Во-первых, давайте посмотрим на результаты опроса, которые отображают наиболее часто используемые языки программирования в 2020 году.  Здесь можно видеть, что одним из наиболее часто используемых языков является Java. И несмотря на то, что Kotlin все еще набирает обороты, сообщество Java все же в несколько раз больше, чем сообщество Kotlin, и, скорее всего, в ближайшее время это не сильно изменится.  Тогда какое это имеет значение? На самом деле имеет, и большое. Так как у Java большое сообщество, то тем, кто работает с Java, гораздо легче найти ссылки или получить помощь, когда это необходимо, как в Интернете, так и в реальной жизни.  Многие компании все еще используют Java в качестве основы, и это может так и остаться в ближайшее время, даже с учетом того, что Kotlin совместим с Java. Обычно переход на новые технологии не происходит для какой-либо одной бизнес-цели, только если у компании нет для этого действительно серьезных оснований.  Подведем итог Для тех, кто решил просто прочитать краткое изложение, вот что мы обсудили: синтаксис: структуры не сильно отличаются, разве что в некоторых мелочах, но Kotlin более гибкий в некоторых моментах; лямбда-выражения: синтаксис практически одинаковый, но Kotlin использует фигурные скобки для того, чтобы код было удобнее читать; обработка нулей: для того, чтобы обработку нулевых значений, Java использует класс, а Kotlin – встроенные операторы безопасных вызовов; классы предметной области: Java использует классы с частными переменными и геттерами/сеттерами, а Kotlin обеспечивает их работу с помощью классов данных; глобальные переменные: Java использует ключевое слово static, а Kotlin – что-то на подобие подклассов; параллелизм: Java использует многопоточность, а Kotlin – сопрограммы (которые обычно более легковесные); функции расширения: это новая функция, введенная в Kotlin, которая позволяет легко добавлять функции в классы, при этом не расширяя их; сообщество: здесь Java по-прежнему лидирует, что помогает в его изучении и получении необходимой помощи. Есть множество других особенностей, которые мы могли бы сравнить. Но то, что мы здесь уже обсудили, я считаю, является основным и самым важным.  На сегодняшний день, я думаю, Kotlin стоит того, чтобы обратить на него внимание. С точки зрения разработки, это поможет избавиться от длинного шаблонного кода и сделать его понятным и коротким. Если вы уже программируете на Java, то изучение Kotlin должно легко вам даться, и вы можете с этим не спешить. 
img
Сегодня хотим поговорить про модуль «Web Callback» для FreePBX 13. Модуль является платным и стоит $50. Платеж единоразовый. В сравнении с популярными сервисами обратного звонка, покупка модуля окупается в среднем за полгода. Интересно? Тогда читайте ниже: настройка и адаптация стиля под свой сайт. Процесс настройки Данный модуль находится в меню Applications. Он позволяет легко и просто добавить HTML “Позвоните Мне” код на ваш веб-сайт. Посетители просто вводят свой телефонный номер для соединения с нужной вам очередью или ринг-группой. Далее, этот модуль позволяет выставить префикс для поступающего номера, что позволит определить, что вызов идет именно с модуля обратного звонка. Так же можно указать правила набора номера, для определения номеров, на которые можно совершить вызов. Как только вы установите направление для вызова и подтвердите настройки модуля, вы получите HTML-код для добавления на вашу страницу. Итак, пошаговый процесс создания кода для помещения на веб-страницу: Нажмите на + Add Web Callback Заполните поля: Описание полей: Name – Название коллбэка CID Prepend – Префикс при определении номера, в данном случае – «CALLBACK» Number Prepend – Префикс при наборе номера Dial Matches – Маска, для определения номеров, которые можно набирать Icon – Выбор иконки из предложенных Valid Message – Сообщение, которое высвечивается при правильном наборе Invalid Message – Сообщение, которое высвечивается при неправильном наборе Error Message – Сообщение, которое высвечивается, если произошла какая-либо ошибка Destination – Направление вызова, в данном случае – ринг-группа с названием “web callback” HTML Code – Код, который появится после сохранения настроек Нажмите Submit Далее нужно только добавить получившийся код на сайт и пользоваться. Только надо учитывать два момента: первый – данный модуль надо купить у Shmooze и иметь публичный адрес вашей АТС/или пробрасывать порты. Изменения стиля формы обратного звонка После того как мы создали форму обратного звонка на сайт, нам необходимо доработать ее внешне, так как встроенные формы имеют не привлекательный дизайн. Открываем файл /etc/schmooze/wcb.html и добавляем в него следующий код: <style type="text/css"> #frame { background-image: url('/admin/images/webcallback.png'); background-repeat: no-repeat; background-size: 200px; height: 65px; cursor: pointer; cursor: hand; } #webcallbackinput { position: relative; left: 66px; top: 30px; width: 125px; } </style> <div id="frame"> <input type="text" name="num" placeholder="Укажите ваш номер" id="webcallbackinput" value=""> <input type="hidden" id="dest" value="http://1.2.3.4:12345/wcb.php"> <input type="hidden" id="i" value="1"> </div> <div id="link"></div> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $('#frame').click(function(){ if ($('#webcallbackinput').val()) { var valid_msg = 'Спасибо. Мы уже звоним Вам!'; var invalid_msg = 'Ошибка. Пожалуйста, укажите все параметры согласно требованию полей'; var but = $(this); $.ajax({ url: $('#dest').val(), type: 'post', data: {p: $('#webcallbackinput').val(), i: $('#i').val()}, cache: false, success: function(data, b, c) { data = $.parseJSON(data); switch (data.Response) { case 'Error': switch (data.Message) { case 'Originate failed': alert(invalid_msg); break; default: alert(data.Message); break; } break; case 'Success': alert(valid_msg); break; default: break; } }, error: function(a, b, c) { alert(invalid_msg); } }) } }) }); </script> Обратите внимание, чтобы форма работала корректно, вам необходимо указать корректное значение параметра value в поле input ниже (это значение было сгенерировано на этапе настройки в поле) и значение параметр id в поле, следующем следом за ним. В нашем примере, id=2: <input type="hidden" id="dest" value="http://1.2.3.4:12345/wcb.php"> <input type="hidden" id="i" value="1"> В данном примере указано значение http://1.2.3.4:12345/wcb.php , где значение 1.2.3.4 – внешний IP – адрес нашего маршрутизатора, а 12345 – это проброс нестандартного порта в наш Asterisk. Рекомендуем в настройках проброшенного порта указать разрешенные сети (source address), с которых можно подключиться через этот порт. Это необходимо в целях безопасности, если ваш Web – сервер находится не в локальной сети, а например, на хостинге Так же здесь вы можете настроить сообщения, которые будут показаны пользователю при успешном и неуспешном исходе вызова обратного звонка var valid_msg = 'Спасибо. Мы уже звоним Вам!'; var invalid_msg = 'Ошибка. Пожалуйста, укажите все параметры согласно требованию полей ';
img
Продолжаем говорить про модули FreePBX. Сегодня спешим рассказать про очень важный модуль - Asterisk Sip Settings. Корректная настройка этого модуля имеет сильно влияет на параметры прохождения голосового трафика и проблем односторонней слышимости. От слов к делу. Вкладка General SIP Settings Перейдем к настройке. Для этого, открываем Settings → Asterisk Sip Settings. Пробежимся по опциям, которые доступны для настройки: Allow Anonymous inbound SIP Calls - если данная опция переключена в позицию Yes, ваша IP – АТС Asterisk будет обрабатывать звонки, поступающие с неизвестных IP –адресов в контексте from – pstn. Обычно, выбор данной опции в положение Yes связан с включением набора по SIP URI. Учтите, что включенная опция значительно увеличивает риски связанные с безопасностью системы; External Address - в данном поле необходимо указать ваш внешний IP – адрес. Помимо прочего, нажав на Detect Network Settings АТС автоматически определит параметры внешнего IP – адреса и внутренних локальных подсетей; Local Networks - локальные подсети, из которых будут подключаться ваши SIP – устройства. Синтаксис прост: сетевой IP – адрес/маска. Например, 192.168.1.0/255.255.255.0. Есть возможность добавить несколько подсетей нажав на кнопку Add Local Network; RTP Settings RTP Ranges -начальный и конечный UDP порты для RTP трафика. В целом, данный диапазон можно посчитать. Знайте, что для каждого звонка нужно иметь по крайне мере 4 порта; RTP Checksums - подсчитывать ли контрольную сумму для UDP, который переносит RTP трафик (голос); Strict RTP - данная опция будет отбрасывать RTP пакеты, который приходят не от источника RTP – потока в рамках сессии; Codecs - выберите нужные кодеки. Важно учесть порядок кодеков – он влияет на приоритет установления кодека в рамках SDP сообщений; STUN Servers - указать IP – адрес STUN сервера. Если кратко, STUN помогает преодолеть проблемы с NAT – он помогает SIP – клиентам внутри локальной сети определять свой публичный адрес; Вкладка Chan SIP Settings Переходим к настройке chan_sip. NAT Settings NAT - настройка NAT (Network Address Translation) для Asterisk. yes - использовать NAT; no - использовать трансляцию согласно RFC3581. Если кратко, то данный RFC позволяет отправлять ответа на порт, с которого запрос был получен, вместо порта, взятого из заголовка Via в SIP пакете; never - не использовать NAT согласно RFC3581; route - данная опция подойдет для клиентов, которые не отрабатывают поле rport в заголовках SIP сообщений (согласно RFC3581 ); IP Configuration - в данном поле вы можете указать параметры внешнего IP. Вы можете указать вручную ваш внешний IP – адрес, а также использовать DDNS (Dynamic DNS); Audio Codecs Non-Standard g726 - порой пир устанавливает порядок инициации параметров аудио потока (характерно для некоторых моделей Sipura и Grandstream) для кодека G726 с полосой пропускания 6, 24, 32, и 40 килобит/сек. Если требуется, установите эту опцию в положение Yes; T38 Pass-Through - позволяет сквозное пропускание факсов через Asterisk без дополнительной обработки и внесения изменений по протоколу T38; No - выключить сквозной режим; Yes - включает T38 в режиме коррекции ошибок FEC (Forward Error Correction), а так же переписывает значение, предоставленное оконечным устройством, согласно которому мы можем отправить факс – пакеты размером 400 байт по протоколу T38; Yes with FEC - включает T38 в режиме коррекции ошибок FEC; Yes with Redundancy - включает T38 в режиме отказоустойчивой коррекции ошибок FEC; Yes with no error correction - включает T38 без коррекции ошибок; Video Codecs Video Support - включив эту опцию в переключатель Enabled, вам будет предложено настроить кодеки для видео – звонков.; TLS/SSL/SRTP Settings Enable TLS -включить поддержку защищенных подключений по TLS; Certificate Manager - включить сертификат для поддержки TLS. Его можно легко настроить в модуле Certificate Manager; SSL Method - метод передачи SSL транспорта (только для TLS). По умолчанию используется sslv2; Don't Verify Server - не запрашивать проверку сертификата сервере (настройка влияет только на TLS).; MEDIA & RTP Settings Reinvite Behavior - опция, которая позволяет перенаправить поток данных RTP в случае, если пир находится не за NAT (средствами RTP это можно детектировать по IP – адресам); RTP Timeout - сброс канала, на котором отсутствует голосовые потоки (пакеты) RTP/RTCP в течение указанного времени. Важно отметить, что постановка вызова на hold не является триггером для данного поля настройки.; RTP Hold Timeout -сбросить звонок, поставленный на удержание после истечения таймера (в секундах) этого поля; RTP Keep Alive - отправлять Keep Alive сообщения (проверки жизнеспособности сервиса) для поддержки NAT – сессии (в случае постановки вызова на удержание особенно актуально); Notification & MWI MWI Polling Freq - частота в секундах, в рамках которой будет производиться проверка смены статуса MWI (световая индикация, Message Waiting Indication) и отправка статуса пирам; Notify Ringing - опция позволяет контролировать состояние абонента, понимая, что его телефон используется (INUSE) получением пакета SIP 180 RINGING. Удобно при использовании BLF функционала; Notify Hold - контроль абонента и перевод в состояние INUSE, если звонок поставлен на удержание (событие ONHOLD).; Registration Settings Registration Timeout - таймаут регистрации. По умолчанию, равен 20 секунд. Иными словами, каждые 20 секунд будет отправляться запрос на регистрацию, пока не будет превышено максимальное количество попыток; Registration Attempts - количество попыток регистрации, после которого сервер примет решение перестать отправлять запросы. Если выставлено как 0, то количество запросов ограничено не будет. В нормальной ситуации, значение 0 является вполне рабочим – Asterisk будет продолжать посылать запросы на регистрацию до тех пор, пока очередная попытка не увенчается успехом; Registration Minimum Expiry - минимальное время, в течение которого сессия регистрации будет считаться просроченной; Registration Maximum Expiry - максимальное время, в течение которого сессия регистрации будет считаться просроченной (для входящих регистраций); Registration Default Expiry - длительность входящих и исходящих регистраций по умолчанию; Jitter Buffer Settings Enable Jitter Buffer - данная опция активирует использование джиттер буффера на принимающей стороне в рамках одного SIP – канала; Advanced General Settings Default Context - контекст обработки вызова по умолчанию, если не указан иной контекст. Сам по себе FreePBX назначает данную опцию как from-sip-external. Вносите изменения только в том случае, если полностью понимаете, что делаете; Bind Address - в данном поле указывается IP – адрес, на котором Asterisk будет ожидать запросы на телефонный процессинг, на порту, указанном в опции Bind Port. Если указано как 0.0.0.0, Asterisk будет принимать запросы на всех адресах, указанных в настройках ОС. Рекомендуем оставить эту опцию без изменений. Кстати, chan_sip не поддерживает IPv6 для транспорта UDP. Если укажите [::], Asterisk будет слушать все IPv4 и все IPv6 адреса. Если вы настолько круты, что используйте PJSip, то смело используйте IPv6 :) Bind Port - локальный UDP (и TCP, если включено в опции Enable TCP) порт, на котором Asterisk слушает обращения к chan_SIP. Если оставить поле пустым, то по умолчанию будет использован порт 5060 (5160); В более старых версиях FreePBX, использовался порт 5060 (когда только 1 SIP драйвер был в наличии). В более новых, используется 5160); TLS Bind Address - TCP порт на котором Asterisk слушает TLS (защищенные) обращения. Конфигурация вида[::], слушает IPv4 и IPv6 на всех интерфейсах; Важно: мы рекомендуем использовать PJSip для всех коммункаци на базе протокола IPv6; TLS Bind Port - локальной порт для входящих TCP обращений в рамках TLS SIP пакетов; Allow SIP Guests - если установлено в положение Yes, то Asterisk разрешит гостевые SIP звонки и обработает их в контексте from-sip-exernal (или значение дефолтного контекста, если меняли). Переключение в положение No позволит запретить так же и анонимные звонки; Enable SRV Lookup - данная опция сильно зависит от используемой версии Asterisk. В корреляции с версией, SRV функционал имеет свои ограничения; Enable TCP - включить TCP; Call Events - важная опция если вы работаете с AMI (Asterisk Manager Interface). При включенной опции, вы сможете мониторить различных события в AMI, которые генерирует SIP UA (user agent). Данный функционал полезен при разработке собственных приложений. ; Other SIP Settings - прочие SIP – настройки, которые вы можете указать вручную (добавить соответствующее поле и его значение);
ВЕСЕННИЕ СКИДКИ
40%
50%
60%
До конца акции: 30 дней 24 : 59 : 59