Python: Бесплатная отправка SMS через mail.ru

В mail.ru агенте есть возможно одна полезная вещь — отправка до 50 sms в сутки с одного аккаунта. Протокол агента является открытым и его можно посмотреть на сайте, однако выложена не совсем свежая версия и не сказано ни слова об отправке sms, но это не проблема.

И так, смотрим описание протокола. Для начала нам надо подключится к mrim.mail.ru на порт 2042 или 443 и адрес сервера агента и порт в обычном текстовом формате «ip:port». Затем надо отправить пакет MRIM_CS_HELLO. Как сказано в описании, заголовок пакета имеет следующий формат:

Размер u_long’а — 4 байта. Первое значение magic соответствует числу 0xDEADBEEF — своеобразный юмор разработчиков, если смотреть на это значение как на строку, то перевод будет что-то вроде «мёртвая говядина». Однако разработчики mail.ru пошли дальше, что бы нас ещё больше рассмешить они решили отправлять все числовые значения задом наперёд, т.е. нам надо записать в заголовок не 0xDEADBEEF, а 0xEFBEADDE (пишем последний байт — EF, предпоследний — BE и т.д.). Строковый переменные отправляются в нормальном порядке. Всё это можно узнать запустив какой-нибудь снифер и посмотрев что шлёт официальный агент.

В итоге, что мы должны послать на сервер:
magic = 0xEFBEADDE (соответствует 0xDEADBEEF)
proto = 0x07000100 (первые два байта — старшая версия протокола, вторые — младшая, 0x00010007)
seq = 0x01000000 (соответствует 0x000001)
msg = 0x01100000 (MRIM_CS_HELLO — 0x00001001)
dlen = 0x00000000 (длинна данных)
from = 0x00000000 (ip адрес клиента, указывать не обязательно)
fromport = 0x000000 (порт клиента, указывать не обязательно)
reserved[16] = 0x00000000000000000000000000000000 (зарезервировано)

В ответ мы должны получить пакет с тем же номером последовательности (seq) и типом (msg) MRIM_CS_HELLO_ACK.

Оформим всё это в виде кода. Создаём файл packets.py в котором будут храниться определённые протоколом константы и класс packet с методами:

setHost(host, port) для установки адреса и порта с которого происходит подключение к серверу (этот метод не очень то нужен);
getPacket(msg) — устанавливает тип пакета равный MSG, увеличивает текущий номер последовательности на 1 и возвращает готовый пакет для отправки в текстовом формате;
setPacket(p) — метод устанавливает из полученного пакета все свойства в классе, если в p содержится поле данных, то оно записывается в свойство data;
addRawData(data) добавляет к свойству data переданный параметр data и обновляет значение длины данных;
addLPSData(data) — добавляет к свойству data длину прикрепляемых данных (в обратном порядке, как и все числовые значения в протоколе) и сами данные;
addULData(data) — прикрепляет «цифровые» данные к пакету в обратном порядке;
clear() — сбрасывает свойства data, dlen, msg;
__d2s(d) — переводит число d в строку для передачи через сокеты на сервер;
__s2d(s) — обратный метода для __d2s(d);
Вот как всё это безобразие выглядит:

Теперь создадим файл emails.py в котором у нас будут храниться функции, связанные с обработкой аккаунтов. Для хранения информации об аккаунтах я выбрал sqlite. На это меня побудило 2 причины: 1 — это удобно :), 2 — мне надо было разобраться особенностями работы с СУБД из-под python (в частности, надо было разобраться с sqlalchemy).

В emails.py у нас содержится описание таблицы с нашими аккаунтами, в которой будут следующие поля:

login — собственно сам email;
password — пароль от него;
limit — по умолчанию равен 50 — количество sms, которое можно отправить в сутки с аккаунта (mail.ru не позволяет больше);
last_sms — время отправки последней sms, mail.ru запрещает слать sms чаще, чем раз в минуту;
А так же следующие функции:

add_emails(emails, session) — открывает файл с мыльниками и паролями (разделены пробелами) и добавляет их в нашу базу, если таких аккаунтов там ещё нет, возвращает число добавленных аккаунтов;
update_limits(session) — устанавливает лимит sms равный 50 у тех аккаунтов, у которых со времени последней отправки sms прошло больше суток;
Код у меня выглядит так:

i

Теперь пришло время заняться самим клиентом. Создаём очередной файл clnt.py и описываем в нём класс со следующими методами:

connect() — создание сокета и подключение к серверу;
hello() — отправка MRIM_CS_HELLO и получение в ответ MROM_CS_HELLO_ACK;
auth(login, passwd) — отправка запроса на авторизацию;
sendSMS(number, text) — отправить sms на номер number. Стоит отметить, что тип пакета с sms — 0x00001039, сам пакет имеет следующий формат — UL 0x00000000 LPS +71234567890, LPS текст sms, где +71234567890 — номер на который мы отправляем сообщение. Так же ещё одна особенность — если среди наших контактов в агенте нет ни одного с указанным номером телефона, то сообщения не будут доходить;
send(msg) — пишет msg в уже открытый сокет;
get() — читает из сокета и возвращает результат;
close() — закрывает сокет
start(login, passwd) — вызывает последовательно все необходимые методы для подключения к серверу;
__init__(rhost, rport) — конструктор, ему передаётся ip-адрес и порт к которому будет производиться подключение;

Все основные функции и классы написаны, осталось их только использовать. Создаём главный файл sms.py.
Первое, что мы в нём делаем — это «обновляем» лимиты в 50 сообщений в сутки, далее проверяем количество аргументов командной строки, если оно равно трём, то считаем, что нам переданы номер на который надо отправить sms и текст (т.е. программа была запущена так: sms.py +71234567890 «testovoe soobshenie»), иначе входим в интерактивный режим.

Следующий шаг — пытаемся подключиться к адресу «http://mrim.mail.ru:2042» и узнать адрес сервера к которому нам рекомендуют подключаться на данный момент.

Выбираем из нашей БД список ящиков у которых не превышен лимит, упорядочиваем (по возрастанию) по дате отправки последнего сообщения и отправляем sms с 1го, уменьшая лимит этого ящика на 1. Если со времени последней отправки sms не прошла ещё 61 секунда, то перед отправкой ждём.

На этом наша программа таки уже закончена. Стоит отметить что не проверяется длина сообщений и отправка кириллицы протестирована только в кодировке CP1251

Понравилась статья? Поделиться с друзьями:
Комментариев: 3
  1. topinambur

    Спасибо за полезную статью! Пошел переписывать на PHP :) :idea:

  2. elisk

    Статья супер!

  3. pethead

    ./sms.py
    File «./sms.py», line 24
    print «%d new emails added.» % add_emails(ef, session)
    ^
    SyntaxError: invalid syntax

    Должно быть
    print («%d new emails added.» % add_emails(ef, session))

    raw_input заменен на inpu

    не хочет

    Error. Can’t connect to mrim.mail.ru.

    Хотя в браузере нормально выплевывает адрес

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: