Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 3766 сообщений
Cообщения с меткой

sms - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

Организация аутентификации по СМС по примеру Telegram/Viber/WhatsApp

Четверг, 14 Июля 2016 г. 19:49 (ссылка)

Представим, что перед вами стоит задача организовать аутентификацию пользователя (в мобильном приложении, в первую очередь) так, как это сделано в Telegram/Viber/WhatsApp. А именно реализовать в API возможность осуществить следующие шаги:




  • Пользователь вводит свой номер телефона и ему на телефон приходит СМС с кодом.

  • Пользователь вводит код из СМС и приложение его аутентифицирует и авторизует.

  • Пользователь открывает приложение повторно, и он уже аутентифицирован и авторизован.



Я постараюсь кратко изложить выработанный подход к этому вопросу. Подразумевается, что у вас API, HTTPS и, вероятно, REST. Какой у вас там набор остальных технологий неважно. Если интересно — добро пожаловать под кат.



Изменения в API



В сущности требуется добавить два добавить три метода в ваше API:




  • Запросить СМС с кодом на номер, в ответ — токен для последующих действий.



Действие соответствует CREATE в CRUD.



    POST /api/sms_authentications/
Параметры на вход:
phone
Параметры на выход:
token


Если всё прошло, как ожидается, возвращаем код состояния 200.



Если же нет, то есть одно разумное исключение (помимо стандартной 500 ошибки при проблемах на сервере и т.п. — некорректно указан телефон. В этом случае:



HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: PHONE_NUMBER_INVALID.




  • Подтвердить токен с помощью кода из СМС.



Действие соответствует UPDATE в CRUD.



    PUT /api/sms_authentications//
Параметры на вход:
sms_code


Аналогично. Если всё ок — код 200.



Если же нет, то варианты исключений:




  1. Некорректный токен: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOKEN_INVALID.

  2. Некорректный код: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: SMS_CODE_INVALID.




  • Форсированная отправка кода повторно.



    PUT /api/sms_authentications//resend


Аналогично. Если всё ок — код 200.



Если же нет, то варианты исключений:




  1. Некорректный токен: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOKEN_INVALID.

  2. Слишком частая отправка (скажем, прошлая отправка была не позднее чем 60 секунд назад): HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOO_OFTEN.



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



Литература:




  1. Образец для подражания — API Telegram: https://core.telegram.org/methods

  2. Дискуссия на SOF: http://stackoverflow.com/questions/12401255/sms-registration-like-in-the-mobile-app-whatsapp



Изменения в коде сервера



Вам потребуется хранить специальный ключ для проверки СМС-кодов. Существует алгоритм TOTP, который позволяет, цитирую Википедию:



OATH-алгоритм создания одноразовых паролей для защищенной аутентификации, являющийся улучшением HOTP (HMAC-Based One-Time Password Algorithm). Является алгоритмом односторонней аутентификации — сервер удостоверяется в подлинности клиента. Главное отличие TOTP от HOTP это генерация пароля на основе времени, то есть время является параметром[1]. При этом обычно используется не точное указание времени, а текущий интервал с установленными заранее границами (например, 30 секунд).


Грубо говоря, алгоритм позволяет создать одноразовый пароль, отправить его в СМС, и проверить, что присланный пароль верен. Причём сгенерированный пароль будет работать заданное количество времени. При всём при этом не надо хранить эти бесконечные одноразовые пароли и время, когда они будут просрочены, всё это уже заложено в алгоритм и вы храните только ключ.



Пример кода на руби, чтобы было понятно о чём речь:



totp = ROTP::TOTP.new("base32secret3232")
totp.now # => "492039"

# OTP verified for current time
totp.verify("492039") # => true
sleep 30
totp.verify("492039") # => false


Существует масса реализацией этого алгоритма для многих языков: для Ruby и Rails, для Python, для PHP и т.д..



Итого, в модели (или в таблице БД, если угодно) надо хранить:




  1. Телефон: phone (советую использовать библиотеки для унификации телефонного номера, вроде этой для Rails),

  2. Ключ для TOTP: otp_secret_key (читаете подробное README для выбранной библиотеки TOTP),

  3. Токен: token (создаете при первом запросе к API чем-нибудь типа SecureRandom),

  4. Ссылку на пользователя: user_id (если у вас есть отдельная таблица/модель, где хранятся данные пользователя).



Особенности реализации мобильного приложения



В случае Android полученный токен можно хранить в SharedPreferences, а для iOS в KeyChain. См. обсуждение на SoF.



Заключение



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


Original source: habrahabr.ru.

https://habrahabr.ru/post/305694/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
Таня_не_Таня

10-07'16

Среда, 13 Июля 2016 г. 18:22 (ссылка)


- Ой, отмена. Мне так нравится ходить по москве и смотреть на дома

- На площадь юных революционеров

- Тань, я опаздываю, где-то в восемь выйду из дома в дибильном костюме

- Я с поднятой рукой у стены

- Отстань от меня

- Ничего не нравится. Я злая.

- У меня нет топора и бензина.

- Я набью себе тату с твоим домофонным кодом.

- Мое фото не потеряй

- Я встретила таксиста на входе в основной кин. Он ходил за мной

- Дим. А коровина идет? Может она песню переделает, а мы споем?

- Да, уже дома ем макароны любимые

- Человек с бутылочкой львовского светлого не может пройти через турникет, поэтому просто смотрит в глаза водителю

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

- А в камине у неё лежала мертвая ворона!!

- Каминская стоит возле элитного алкоголя и кричит "официант"

- Переливаем виски в тц. Скоро.

- Тут значит дама в крю утверждает, что девушек вперед пропускали, чтобы их волки съедали. Она крайне пьяна.

- Итак. Проезжаю район отрадное. "Дно" выделили другим цветом на световой вывеске.

- Я сдала экз. Меня преследует серов.

- Я вот буду соглашаться теперь на все нелепое.

- Добрый негр по имени Дик.

- Хм, в Зелеке мрачный мрак

- Мужчина за 5 минут в автобусе успел познакомиться с соседкой, попеть и перекреститься.

- Хочу паблик, где выкладывают фотографии людей, зевающих с закрытыми ртами.

- Я чего-то понять не могу - Лобачевский красавец или нет.

- Я блiз мiрскиi замка

- Думали это кошка мужик, а оказалась девочка кот

- Выключили беспечного ездока и пошли хоронить боно

- В моих широких штанах вино можно проносить на концерты. К ноге приматывать.

- Ку-ку)

- Я в нестабильном состоянии перед днём рождения)

- Еву покормишь

- И где это - около баптистов

- Мы пропустили вечеринку СВЕЖАЯ КРОВЬ

- Твое соо пришло под песню "поклонение мертвым"

- Ты ушла из дом пить сидр под дождем?

- Все из-за вичухи :(

- Не зря я парк культуры ненавижу

- К онеш

- Пхахаха я проспала остановку и начала бить тревогу и вызывать убер)

- Жесть вхпха меня сейчас довёл до дома какой-то парень ! Я спрашивала в каком мы районе и далеко ли уехали от Щелковской

- Я заснула вообще в семь часов) это ты с городского звонила?

- Я не уйду)

- Меня мучает вопрос. Фотографию с куполами можно выкладывать или это пропаганда алкоголизма? Хаха

- Это корень, корень имбиря

- Фирсановка. Остановка "кладбище"

- Таков божий промысел. Пусть пятерых давит.

- Ку-ку. У тебя все в порядке?

- Тут чувак очень плохо играет на саксофоне и женщина вышла из метро и спросила у меня, где вход

- О, это прекрасно) а какие экстремисты?

- Хорошо, засушу)


Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Как работает умный обработчик служебных смс (показывает только важную информацию)

Вторник, 28 Июня 2016 г. 08:34 (ссылка)

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

Приложение парсит входящие смс-ки и показывает только важную информацию из них.

Показывает красиво, быстро и удобно.



1. Как это работает



В манифесте прописываем разрешение на получение и чтение SMS




`


Там же регистрируем receiver

Разрешение action_sms_received_test нужно для тестирования.

Чтобы не тратить деньги на настоящие смс во время тестирования, я отправляю Intent с этим action из приложения и ловлю его.











Теперь ресивер будет получать все входящие сообщения



@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_SMS_RECEIVED:
handleIncomingSms(context, intent);
break;
case ACTION_SMS_RECEIVED_TEST:
// do test
break;
}
}


Теперь в методе handleIncomingSms(context, intent); требуется разобраться, что за СМС нам пришла, и принять решение о том, что делать.

Если она является служебной — мы её разбираем, достаем полезную информацию, и отображаем её в красивом виде.

Каким образом мы понимаем, служебная она или нет — опишу позже.



Грубо, это выглядит так



private void handleIncomingSms(Context context, Intent intent) {
L.i("handleIncomingSms");
Bundle bundle = intent.getExtras();

if (bundle == null) {
return;
}

try {
Object[] pdus = (Object[]) bundle.get(PDUS);
String smsText = "";
for (Object pdu : pdus) {
final SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
smsText += message.getMessageBody();
}
checkTemplates(context, smsText);
} catch (Exception e) {
L.i("handleIncomingSms - Exception", Log.getStackTraceString(e));
}

}


Метод checkTemplates();



private void checkTemplates(Context context, String smsText) {
L.i("checkTemplates", smsText);

// get templates
List smsTemplates = DatabaseManager.getSmsTemplates();

if (smsTemplates == null) {
return;
}

// check if sms text according to some template
for (SmsTemplate smsTemplate : smsTemplates) {
List messageLines = SmsNewParser.getMessageLines(smsTemplate, smsText);
if (messageLines != null) {
Sender sender = DatabaseManager.getSender(smsTemplate.sender);
showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : "");
}
}
}


Метод showPopupDialog



private void showPopupDialog(Context context, List message, String iconUrl) {
L.i("showPopupDialog", message, iconUrl);

Intent popupIntent = new Intent(context, PopupActivity.class);
popupIntent.putExtra(PopupActivity.ICON_URL, iconUrl);
popupIntent.putExtra(PopupActivity.MESSAGE_0, message.get(0));
popupIntent.putExtra(PopupActivity.MESSAGE_1, message.get(1));
popupIntent.putExtra(PopupActivity.MESSAGE_2, message.get(2));
popupIntent.putExtra(PopupActivity.MESSAGE_3, message.get(3));
popupIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);

context.startActivity(popupIntent);

}


После этого пользователь видит такой экран

Смысл в том, чтобы быстро увидеть полезную информацию



image



2. Алгоритм распознавания СМС и выдачи важной информации



2.1. Кратко




  • На сервере есть шаблоны

  • В каждом шаблоне указано а) как должна выглядеть СМС б) что именно показывать для неё

  • Приложение при каждом запуске синхронизирует их

  • Каждое входящее сообщение прогоняется по всем шаблонам

  • Если найден шаблон, которому она соответствует — показывается важная информация в нужной форме



2.2. Подробно о модели



Шаблон выглядит так



{
"sender": "bank_alfa",
"text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43",
"mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~",
"lines": [
{
"line": "EXTRA_PURCHASE"
},
{
"line": "SUM_0"
},
{
"line": "EXTRA_TOTAL"
},
{
"line": "SUM_1"
}
]
}



  • sender — отправитель

  • text — начальный текст настоящей смс. может быть использован для тестов

  • mask — сам шаблон. используются служебные слова вида ~FOO~

  • lines — строки сообщения, которое будет выдаваться на экран. В них можно указывать части шаблона, а можно использовать слова, которых нет в шаблоне.



Служебные слова делятся на extra и обычные.

Extra означает, что их нет в шаблоне.

Примеры:



~SUM~ — обычное служебное слово. Означает выражение с цифрами, разделенное точкой или запятой.

Используется для определения суммы денег. Для его поиска используется regex



{
"name": "SUM",
"regex": "\\d+[.,]{0,1}\\d+",
"values": [],
"is_extra": false
}


~CURRENCY~ — обычное слово, которое может принимать несколько значений. Для его поиска используется перебор его значений.



{
"name": "CURRENCY",
"regex": "",
"values": [
{
"value": "usd"
},
{
"value": "rur"
},
{
"value": "eur"
},
{
"value": "rub"
}
],
"is_extra": false
}


~EXTRA_CODE_WORD~ — служебное слово типа extra. Используется для вывода текста "Кодовое слово" в результате.



{
"name": "EXTRA_CODE_WORD",
"regex": "",
"values": [
{
"value": "Кодовое слово"
}
],
"is_extra": true
}


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

Эта информация хранится в объектах sender.



Пример:

Это Альфа банк и его иконка.



{
name: "bank_alfa",
icon_url: "https://dl.dropboxusercontent.com/u/1816879/CaptainSms/logo_alfa.png"
}


В итоге не сервере хранится




  • Шаблоны

  • Служебные слова

  • Отправители



Полный json можно посмотреть здесь



2.3. Подробно об алгоритме



Мы скачиваем модель, сохраняем её.

Дальше следует сама процедура разбора смс и создание результирующего сообщения.



Для парсинга текста сообщения я использую класс SmsParser со статичными методами.

Главный метод — getMessageLines(SmsTemplate smsTemplate, String realSmsText)

Он возвращает строки сообщения, если все ок, или null, если мы не нашли подходящий шаблон.

Этот метод вызывается из этого места метода checkTemplates, приведенного выше.



    // check if sms text according to some template
for (SmsTemplate smsTemplate : smsTemplates) {
List messageLines = SmsNewParser.getMessageLines(smsTemplate, smsText);
if (messageLines != null) {
Sender sender = DatabaseManager.getSender(smsTemplate.sender);
showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : "");
}
}


Мы проходим по всем шаблонам из базы и пытаемся для каждого взять message lines.

Если получилось — показываем экран с информацией..



Логика getMessageLines кратко

Бежим по маске и сравниваем её посимвольно с текстом смс, записывая в массив значения встретившихся служебных слов, или выкидывая nullесли встретили несоответствия



Логика getMessageLines подробнее:




  • Бежим посимвольно по тексту маски

  • Если символ — это начало служебного слова (~), то:

    — Понимаем, что это за слово (например, ~SUM_0~)

    — Вычисляем его значение в тексте СМС (например, 255.00)

    — Отрезаем от маски это слово, а от текста это значение (чтобы дальше бежать посимвольно)

  • Иначе, если это простой символ, то:

    — Если они совпадают в максе и тексте, то отрезаем их оттуда и оттуда чтобы дальше сравнивать

    — Если они разные, то выкидываем null — текст не подходит под шаблон



Логика с примерами кода



Как параметры, в метод нам приходят шаблон и текст смс



public static List getMessageLines(SmsTemplate smsTemplate, String smsText)


В начале метода инициализируем лист служебных слов. В базу они попали из регулярного обновления с апи.

Нам нужна глобальная переменная, т.к. метод большой и разбит на части.



private static void initReservedWords() {
L.i("initReservedWords");
mReservedWords.clear();
mReservedWords = DatabaseManager.getReservedWords();
}


Затем создаем список служебных слов из заданного шаблона.



    List reservedWords = new ArrayList<>();
for (SmsTemplateLine line : smsTemplate.lines) {
reservedWords.add(getReservedWordByName(line.line));
}


т.е. если у нас есть шаблон



{
"sender": "bank_alfa",
"text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43",
"mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~",
"lines": [
{
"line": "EXTRA_PURCHASE"
},
{
"line": "SUM_0"
},
{
"line": "EXTRA_TOTAL"
},
{
"line": "SUM_1"
}
]
}


то мы хотим получить список




  • EXTRA_PURCHASE

  • SUM_0

  • EXTRA_TOTAL

  • SUM_1



далее идет основная логика



    // check match symbol by symbol
try {

do {
String s = mask.substring(0, 1);
if (s.equals(ReservedWord.SYMBOL)) {

// found start of a reserved word
ReservedWord currentReservedWord = getFirstReservedWord(mask);
String valueOfCurrentReservedWord = getValueOfReservedWord(smsText, mask, currentReservedWord);

// add value in the list, if reserved word is in the list
if (reservedWords.contains(currentReservedWord) && valueOfCurrentReservedWord.length() > 0) {
values.put(currentReservedWord.getForm(), valueOfCurrentReservedWord);
}

// cut text and mask to look next symbols
smsText = smsText.substring(valueOfCurrentReservedWord.length());
mask = mask.substring(currentReservedWord.getForm().length());

} else if (s.equals(smsText.substring(0, 1))) {

// that symbols matches, go to the next symbol
smsText = smsText.substring(1);
mask = mask.substring(1);

} else {
/*
* that symbol does not match, so text not match that mask, so method fails
* because we cannot return correct values according to that list of reserved word
*/
return null;
}
} while (mask.length() > 0);
} catch (StringIndexOutOfBoundsException e) {
/*
* There is some error during parsing.
* That mean text does not match mask.
*/
L.i(TAG, "getMessageLines - Exception - " + Log.getStackTraceString(e));
return null;
}


Она делает ровно то, что описано выше, как "Логика getMessageLines подробнее:"



Далее мы пересортировываем список, т.к. в тексте он встречается в другом порядке, чем наших message lines



    // convert list to the right order
List valuesList = new ArrayList<>();
for (ReservedWord word : reservedWords) {
LLog.e(TAG, "getMessageLines - return list - " + values.get(word.getForm()));
if (values.get(word.getForm()) != null) {
valuesList.add(values.get(word.getForm()));
}
}


Далее мы добавляем служебные слова типа extra, т.к. мы их не находили при прохождении по тексту смс.



    // add values of all the extra words
for (int i = 0; i < reservedWords.size(); i++) {
if (reservedWords.get(i).isExtra) {
valuesList.add(i, reservedWords.get(i).values.iterator().next().value);
}
}


Это нужно вот почему.

На вход нам подали smsTemplate. У него есть набор messageLines. Например, их было 4.



  "lines": [
{
"line": "EXTRA_PURCHASE"
},
{
"line": "SUM_0"
},
{
"line": "EXTRA_TOTAL"
},
{
"line": "SUM_1"
}
]
}


Но в процессе проверки текста на совпадение с шаблоном мы нашли только SUM_0 и SUM_1

Т.к. это данные, которые реально есть в тексте СМС.

Таким образом, после первого куска логики мы имеем массив из двух элементов (в данном случае 212,30 и 20537,96).

Но на выход нам нужно подать 4 строки (к этим двум нужно еще добавить EXTRA_PURCHASE и EXTRA_TOTAL), причем в нужном порядке.

Поэтому в конце метода мы их добавляем.



В итоге, на выходе мы получаем массив из четырех строк.



Например, если у нас был шаблон



{
"sender": "bank_alfa",
"text": "3*8272; Pokupka; Uspeshno; Summa: 212,30 RUR; Ostatok: 20537,96 RUR; RU/MOSKVA/GETT; 15.04.2016 06:02:43",
"mask": "~N~*~N4~; ~BANK_ACTION_0~; Uspeshno; Summa: ~SUM_0~ ~CURRENCY_0~; ~BANK_ACTION_1~: ~SUM_1~ ~CURRENCY_1~; ~WORD~; ~N2~.~N2~.~N4~ ~N2~:~N2~:~N2~",
"lines": [
{
"line": "EXTRA_PURCHASE"
},
{
"line": "SUM_0"
},
{
"line": "EXTRA_TOTAL"
},
{
"line": "SUM_1"
}
]
}


то на выходе мы получим




  • Покупка

  • 212,30

  • Осталось

  • 20537,96



На этом главная логика заканчивается.

Далее мы просто показываем это в нашей попап активити таким методом



showPopupDialog(context, messageLines, sender != null ? sender.iconUrl : "");


Текст messageLines просто отображается в текст вьюшках.

iconUrl подгружается в image view с помощью Glide — тут все предельно просто.



Заключение



Очевидно, что алгоритм примитивен и может быть улучшен.

Из идей




  • разбить api на разные json файлы (например один json для каждого отправителя)

  • умный алгоритм прогона по шаблонам (сначала все с кодами — они нужны быстрее всего, затем часто используемые, затем все остальные)

  • вероятно, можно улучшить сам код парсинга (проверить на создание лишних объектов, уменьшить количество циклов и прочее)



Но поставленную задачу приложние решает.



image



Прилагаю главный класс для парсинга сообщений.

Он немного отличается от кода, приведенного выше,

т.к. приведенный код был улучшен визуально.


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/303990/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Собственный «Кто звонил?» на базе Мультифона

Воскресенье, 06 Марта 2016 г. 07:23 (ссылка)

На какие вопросы вы найдёте ответы в этой статье:




  • как отправлять USSD через GSM/3G/4G-модем и читать ответы;

  • как отправлять SMS через Мультифон;

  • как использовать Яндекс SpeechKit в автоответчике на Asterisk.



На какие вопросы вы не найдёте ответов:




  • зачем нужен собственный «Кто звонил?».



Все понимают механизм сервиса, аналогичного «Кто звонил?»: по недоступности звонок уходит на специальный номер, там его принимают, сообщают звонящему, что абонент недоступен, и отправляют абоненту СМС с информацией о звонке. В приёме звонка с Мультифона на Астериске никаких тонкостей нет.



Вкратце
Регистрация:



register => 79xxxxxxxxx@multifon.ru::79xxxxxxxxx@sbc.megafon.ru/79xxxxxxxxx


Транк:



[multifon](!)
type=peer
insecure=port,invite
host=sbc.megafon.ru
fromdomain=multifon.ru
context=from_multifon
dtmfmode=inband

[multifon-in](multifon)

; Если мы ещё и звонить хотим
[multifon-out](multifon)
defaultuser=79xxxxxxxxx
fromuser=79xxxxxxxxx
secret=




Первый вопрос, который встал передо мной в полный рост…



Как отправить СМС?



Самый простой путь, конечно же, купить человеческий HTTP API у какого-нибудь гейта и задёшево отправлять через него. Но во-первых, это неспротивно. А во-вторых, у меня на тарифе несколько сотен СМС включено в абонплату, и с телефона я их почти не отправляю.

Перепробовав все возможные варианты с MessageSend (и через диалплан, и через АМИ, и через чёрта лысого), а также помурыжив SipSak, я сдался и попробовал скрипт для Yate.

О чудо! СМС через Мультифон отправляются.

Но Yate – слишком тяжеловесный движок, подумал я. Yate должен крутиться демоном и требует совершенно избыточной для этих задач конфигурации, подумал я. Поэтому, изучив перловый скрипт, я вычленил из него главное и вернулся к сипсаку. Главным оказался набор нестандартных заголовков (включая обязательного совершенно определённого юзер-агента, ага):



User-Agent: MCPC-MG-1-0-34-3490/2.0.0.5301
X-Movial-Content: sms/text
X-Movial-DeliveryReport: true
Content-Type: text/plain; charset=ISO-10646-UCS-2


Кстати, кастомный юзер-агент, насколько я понимаю, изначально полностью исключал возможность отправки через Астериск.

На первый взгляд, всё казалось просто. Но сообщения всё равно не уходили. Включив дебаг у сипсака, я понял, что эта скотина имеет набор предопределённых заголовков, которые тупо дублируются, если заданы в командной строке. Пришлось брать в руки напильник.

Обработав сипсак так, чтобы кастомные заголовки он считал приоритетными, а дубли отбрасывал, я добился наконец первого результата. Но моя радость была бы неполной, если бы сипсак корректно кодировал передаваемое сообщение. Расписывать всю эпопею вкостыливания в код, построенный на нуль-терминированных строках, UCS2-BE я не буду, сразу представлю на ваш суд результаты своих трудов: https://github.com/wolandtel/sipsak. Сам процесс отправки СМС я обернул в удобный скрипт. Для комплекта в последнем репозитории есть скрипт settings.sh, который упрощает настройку пароля и маршрутизации (SIP, GSM+SIP, GSM) на мультифоне.

Ну, вот, отправлять СМС мы умеем. Теперь можно настроить сам…



Автоответчик



Здесь нам поможет AGI. Скажу сразу, что от природной лени протокол AGI реализовывать я не стал, а лишь сделал в скрипте такую эмуляцию, которая позволяет выполнить задачу. А именно, скрипт должен дождаться от астериска ответа на команду STREAM FILE, чтобы проигрывание не закончилось раньше времени.



Диалплан
; autoresponder
exten => 79xxxxxxxxx,1,NoOp
same => n,AGI(notify.agi)
same => n,Set(MSG="Абонент умер. Когда воскреснет, может перезвонить. Но хотите ли Вы этого?")
same => n,Answer
; Params: speaker, emotion, robot
; speaker: [мужские]: zahar, ermil; [женские]: jane, omazh.
; emotion: good, neutral, evil, mixed.
; robot: true, false.
same => n,AGI(tts.agi,${MSG},ermil,neutral,true)
same => n,Hangup




Можно было, конечно, отправлять СМС из того же скрипта, которым проигрывается сообщение, но с точки зрения структуры приложения логично было их разнести.



notify.agi
#!/bin/bash

while read line; do
[ -z "$line" ] && break
num=$(echo $line | grep 'agi_callerid: ' | sed 's/agi_callerid: //')
[ -n "$num" ] && break
done < /dev/stdin

[ -z "$num" ] && exit 1

/usr/local/multifon/sms.sh 79xxxxxxxxx "Пропущенный вызов [$(date '+%Y-%m-%d %H:%M:%S')] с номера +$num"




Абсолютно никакого AGI, по факту. Просто делаем, что требуется, и закругляемся.



tts.agi
#!/bin/bash

text="$1"
speaker="$2"
emotion="$3"
robot="$4"

key=
dir=/var/lib/asterisk/tts
url=https://tts.voicetech.yandex.net/generate
dbg=${0/.agi/.log}

fname="$dir/$(echo -n "$text-$speaker-$emotion-$robot" | md5sum | cut -d' ' -f1)"
wav="${fname}.wav"

echo -e "$@" > "$dbg"
while read line; do
echo $line >> "$dbg"
[ -z "$line" ] && break
done < /dev/stdin

[ -f "$wav" ] || /usr/bin/curl -s --data-urlencode "text=$text" \
--data-urlencode format=wav \
--data-urlencode lang=ru-RU \
--data-urlencode "speaker=$speaker" \
--data-urlencode "key=$key" \
--data-urlencode "emotion=$emotion" \
--data-urlencode "robot=$robot" \
"$url" > "$wav" || exit 1

for f in al ul gsm; do
[ -f "${fname}.$f" ] || /usr/bin/sox "$wav" -r8k "${fname}.$f" || exit 2
done

echo -e "STREAM FILE $fname 0"

read line < /dev/stdin
echo $line >> "$dbg"




Здесь тоже нет AGI. Мы только дожидаемся ответа от Астериска командой read, чтобы не закрыться раньше, чем он проиграет сообщение. Прошу обратить внимание на параметр -r8k для сокса. Почему-то без параметров он неправильно выставляет рейт выходному файлу, и сообщение получается замедленным в два раза. Подробности самого Яндекс SpeechKit API и то, как получить ключ, есть в документации.



Основная часть на этом закончена. Но статья будет неполной без описания нюансов.



Подготовка сим-карты



Очевидно, чтобы всё это завелось, потребуется отдельный номер с Мультифоном. Вы покупаете сим-карту и…? Простой и очевидный путь – вставить её в телефон, набрать необходимые запросы и начать использовать. Но мы не ищем лёгких путей. У меня с давних времён валяется пачка 3G-модемов (знаменитый E1550 и чуть менее знаменитые E150), но нет ни одного свободного телефона. А использовать свой для таких операций я не люблю: лишний раз выключать, вскрывать.

Итак, для получения и отправки СМС мы будем использовать давно и хорошо всем известный smstools. Настройку я описывать не буду, ибо боян. Только автодекодинг не включайте. А то будет вам мусор вместо тела сообщения.

Итак, после приобретения сим-карты нужно сделать две вещи:




  • получить пароль для личного кабинета (ибо эти бабуины убили Сервис-гид, и теперь для каждого номера требуется собственный логин);

  • подключить Мультифон (ибо эти бабуины сломали веб-интерфейс, и без USSD не обойтись).



И если с приёмом СМС всё понятно, то USSD – не такая прозрачная вещь.

Перед тем, как продолжить, можете ознакомиться с [репозиторием|(https://github.com/wolandtel/smstools) дополнений к smstools, облегчающих работу с SMS и USSD. В разделе example репозитория есть перловый инструмент работы с USSD, который я использовал для понимания сути кодирования/декодирования символов в USSD. Авторство не указано, и ссылок нет потому, что я не помню, где его взял. Можно использовать его, но лично я не понимаю perl, а техномагию я не люблю. Поэтому чтобы почувствовать контроль над ситуацией я написал утилиты encode и decode. Как мне кажется, код на си куда проще для понимания, поэтому утилиты будут полезны тем, кто тоже захочет знаний.

Подозреваю, что если выставить другую кодировку командой AT+CSCS, проблема отпадёт сама собой, но мы же не ищем…

В остальном работа с USSD проста: модем регистрирует три девайса ttyUSB. Пишем в первый, читаем из последнего.

Для подключения личного кабинета достаточно просто отправить 105: ./ussd.sh '*105#' (подразумевается, что /dev/modem указывает на первый из ttyUSB модема). Дожидаемся СМС с паролем (удобнее всего это делать командой ls -lrt /var/spool/incoming), после чего смотрим ./viewsms .

Мультифон подключается в два этапа: запустив cat /dev/ttyUSB2, отправляем *137#. Копируем ответ и выясняем, что там написано, с помощью decode. А приходит в ответ менюшка, где для подключения нужно выбрать пункт 1. Выполняем ./ussd 1 и получаем в СМС пароль.



На этом можно было бы и завершить, но так как мы имеем дело с бабуинами, стоит упомянуть про…



Организационные моменты




  1. Обязательно укажите, чтобы новую сим-карту подключили на существующий лицевой счёт. Насколько я понял, отключение по неактивности не работает, если на все номера один баланс. Да и вообще удобнее. Когда был жив сервис-гид с возможностью управлять всеми номерами из-под одной учётной записи, этот совет был ещё актуальнее.

  2. В современном мире наебизнеса уже никому не надо объяснять, что сразу после покупки сим-карты необходимо отключать все условно-бесплатные опции (в том числе, «Кто звонил?», ага). Но! Бабуины пошли дальше и некоторые из условно-бесплатных опций не отображаются в личном кабинете. Отключить их можно только звонком в контакт-центр. Желательно подробно прочитать описание тарифа, чтобы выяснить, каких же замечательных вещей вам впарилипродали.

  3. В качестве номера для отправки СМС я использую свой основной номер, потому что на нём есть предоплаченный пакет. Естественно, для этого Мультифон на нём тоже должен быть включен (но совершенно не обязательно держать его постоянно зарегистрированным). Дополнительный номер используется только для приёма звонков.



PS



К сожалению, Мультифон не передаёт номер, переадресовавший звонок. Поэтому, если вы хотите подключить свой бонусный «Кто звонил?» друзьям/родным, придётся на каждого человека покупать отдельный номер под автоответчик. Ещё можно попробовать переадресацию с доп. набором (+79XXXXXXXXXpXX), но я не проверял. Если проверите, пишите.



А вот теперь – всё!





Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/278679/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
anykey13

Без заголовка

Понедельник, 29 Февраля 2016 г. 08:37 (ссылка)
youreklama.ru/prikolnye-sms...?_utl_t=li

Прикольные SMS - Интересные факты новости статьи


1456724236_kopiy (700x700, 119Kb)

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
vsemmoney

SMS рассылки любого объёма и тематики - Купля/продажа и услуги - Форум о заработке в интернете и инвестициях

Вторник, 12 Января 2016 г. 19:28 (ссылка)
vsemmoney.ru/topic/1623-sms...-tematiki/


SMS рассылки любого объёма и тематики - отправлено в Купля/продажа и услуги: Любые смс рассылки в любых объёмах



Оптимальный сро...

Комментарии (0)КомментироватьВ цитатник или сообщество
ru_prnews

Sms2gsm снизил стоимость сообщения до полкопейки

Четверг, 24 Декабря 2015 г. 18:10 (ссылка)


Сервис по рассылке сообщений Sms2gsm выпустил новое приложение для ПК. Его функционал позволяет осуществлять рассылку с большинства популярных гаджетов: телефонов, планшетофонов или модемов.


 




Приложение ориентировано на клиентов, заинтересованных в том, чтобы сделать рассылку сообщений дешевле и удобнее. Дружелюбный интерфейс и простота использования программы от Sms2gsm выделяют ее среди подобных решений. СМС рассылка организована максимально удобно: клиенту нужно подготовить лишь список номеров в формате Excel и создать текст письма.



Стоимость одного сообщения составит всего 0,5 копеек благодаря использованию специальных тарифов, предполагающих месячную абонентскую плату в 180 рублей. Для сравнения, при использовании онлайн-сервисов стоимость одного SMS составит 1,5 рубля. Программа от Sms2gsm способна существенно сэкономить средства пользователей в долгосрочной перспективе. Стоимость приложения составляет 6000 рублей. Это единоразовое вложение, позволяющее сократить расходы на рассылку в несколько раз.



Единственным ограничением является то, что программа не может выполнить подмену номера или каким-либо образом подписать его. При получении СМС клиент будет видеть номер SIM-карты, с которой осуществлялась рассылка.





Особое внимание создатели программы уделили безопасности: приложение работает даже без интернет-подключения. Соответственно, нет необходимости в том, чтобы загружать базу номеров на сторонние сайты для организации рассылки.



Таким образом, использование программы предполагает всестороннюю защиту пользовательских данных, существенную финансовую экономию и полную автоматизацию процесса рассылки.



Контакты:



mail@sms2gsm.ru

http://www.sms2gsm.ru


Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<sms - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda