В предыдущей статье я описал как настроил и собрал GSM <> SIP систему на базе Asterisk. В этой статье расскажу как быть с входящими SMS,  если получатель не в сети (не прошел регистрацию на PBX). Если посмотреть предыдушую статью, видно, что SMS (MESSAGE(body)) преобразуется из BASE64 в plane text на системе с модемом. Это остаток от дебага, когда нужно было видеть что пришло на модем в консоли Asterisk. Я не стал менять этого поведения, так как у меня сохраняется проблема составных SMS (когда сообщение разбито на несколько частей). С ней я буду разбераться позже. Далее MESSAGE(body) передается уже в виде SIP Message на центральную PBX, где обрабатывется согласно dial plan.  И тут возникала проблема - если получатель сообщения (он же extension) был в момент его прихода не в сети - сообщение безвозвратно теряется. Я считаю что GSM у нас есть всегда, по этому не стал заморачиваться с исходящими сообщениями. Сегодня поговорим как создать очередь SIP сообщений, обеспечить их хранение и повторную доставку. У Asterisk есть приложение очереди (message queue). Но само по себе это приложение не может писать Message(body) куда либо "на потом", так как во первой нужно для роутинга голосовых вызовов, а так же доставляемых сообщений (из серии - у нас поток, а приемник медленный или занят). Придется использовать костыль - систему внешних скриптов, которые будут создавать файлы очереди и помещать их в специальную дирректорию, откуда Asterisk их будет читать.  Задача состоит в том, чтоб помещать недоставленные сообщения в call file, который будет вгружаться в очередь, и повторять попытку доставки несколько (N) раз через определенные (T) промежутки времени. Я рекомендую обращаться к справкам по всем упоминаемым мной фишкам Asterisk'а. Читая описание синтаксиса очень часто понятно где что не сработало. Оба действия делаются двумя приложениями: app_system и pbx_spool. Первое - за вызовы скриптов, второе - за обработку call file. Нужно проверять несколько условий: Зарегистрирован ли целевой получатель на сервере. Удалось ли доставить сообщение. Сообщение которое мы пытаемся доставить пришло из очереди, или новое. Так же важно помнить, что так как мы используем скрипт, ему придется передавать аргументы, а по сути - вызывать строчку в shell, что несет серьезные риски. По этому придется опять преобразовать MESSAGE(body) в BASE64 и расшифровывать его обратно перед доставкой в SIP клиент. Не забываем подгрузить func_base64. Ну что, поехали создавать? Все что мы делаем - делаем на центральной PBX. Давайте разберем по блокам. Начало стандартное, там ничего интересного, пока мы не попадаем к строкам Мы проверяем смогли ли мы доставить сообщение. У него есть статус и мы будем продолжать если он не в значении SUCCESS. Первая строка (я не использую) выкидывает нас в блок отправки уведомления отправителю. Так как никакого реального механизма уведомить о судьбе сообщения нет (у нас SMS доставилось на модем), нужно ручками описать процедуру формирования и отправки такого уведомления. Весь блок строк 11-21 я не сильно проверял. Что-то там будет работать, но это здесь чисто на будущее, если я решу это использовать. Второй блок проверяет, откуда это сообщение в контексте и направляет в соответвующие обработчики Вторая строка - отсылка к проверке не пришло ли сообщение из очереди. Нужно понимать, что нам делать с шифрованием BASE64, так как сообщения из очереди у нас зашифрованы (будет видно ниже) Т.е. если переменная INQUEU не в состоянии 1, считаем что сообщение пришло к нам из предыдущего блока, т.е. было передано оконечным PBX, но не доставлено адресату. И да, эта переменная появится значительно позже и будет прочитана из call файла, а установлена - скрпитом. Второй строкой мы генерируем уникальный идентификатор для последующиего использования в сообщении. См строку четыре. Третьей строкой шифруем получателя. Shell обычно не любит всякие там + и < и мы все это превращаем в безобидную абракадабру для дельнейшего использования. Четвертой строкой мы формируем текст, который будет помещен в очередь. Суть проста - когда мы получим сообщение из очереди, в нем будет указана дата-время когда оно в очередь поместилось и сам текст. В простивном случае мы просто получили бы текст сообщения без возможности узнать когда же мы его пропустили. Следующий блок у нас кодирует всю строку сообщения (DATE+Message(body)) вместе в BASE64 для получения единой строки. А далее мы вызываем внешний скрипт управления, которому в качестве аргументов передаем источник, получателя и текст сообщения. Нет. Пока я не показал вам сам скрипт, не ясно зачем у нас есть новый контекст app-fakeanswer Ответ до безобразия прост. Когда сообщение будет обрабатываться message_queue оно попадает в этот контекст и идет проверка на доступность получателя (extension). Если получатель не зарегистрирован на сервере, мы выходим, если зарегистрирован - передаем сообщение обратно в обработку в соответствующий контекст incoming-sms И так, теперь самое интересное. Нам нужно воспользоваться так называемыми AGI приложениями, а по сути - bash скриптом, в котором мы пережуем аргументы, проверим есть ли у нас уже сообщение для этого получателя и создадим call file , который потом положим в специальную дирректорию, из которой его будет вычитывать Asterisk. Оба скрипта закидываем в /var/lib/asterisk/agi-bin/ или туда, где у вас расположена agi директория, а так же не забываем придать им +x (право исполнения) и владельца из-под которого у вас работает Asterisk (chmod & chown). Я модифицировал найденный в сети скрипт несколько адаптировав его под свои пути и исправив пару косяков. Что тут интересного: Строки 20 и 21: задают количество повторов и время между попытками. Строка 50: контекст куда выкидывать сообщения. Строка 56: это установка статуса очереди (переменная INQUEUE), а так же мы указываем путь к временной директории, доступной Asterisk'у для создания временного файла. Строки 113-116: декодируют все аргументы. Строка 138: проверяет а есть ли вообще получатель и если нет, выходит с сообщением об ошибке. Сообщение теряется. Именно тут была основная закоыврка, так как нужно верно вызвать комманду в Asterisk согласно вашему конкретному случаю. Строка 159 и далее: формируют файл, приделывают ему псевдо-случайное название (для правильного порядка в очереди и избегания перезаписи). А очень просто. Есть еще один скрпит, который проверяет поступающие call file'ы и распределяет их по порядку для доставки. Тут нужно не забыть создать еще одну дирркеторию hold, в которой будут храниться call фвйлы до момнета когда их можно перемещать на доставку в папку outgoing. Опять же не забывайте редактировать пути согласно вашим реалиям. И последним, добавляем в crontab следующую строчку Эта штука дергает ежесекундно скрипт проверки и организации очереди. Сохранив ваш extensions.conf идем в астериск и делаем dialplan reload. Отключаем клиент или sip-телефон от сети и шлам сообщение. Подлкючаемся - вуаля (если все сделано верно). P.S. пока что в очереди застреают многосегментные сообщения, как я уже говорил - это на следующий раз. Комментарии и замечания очень приветствуются.