Рейтинг@Mail.ru

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


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

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

u_long	magic;
u_long	proto;
u_long	seq;
u_long	msg;
u_long	dlen;
u_long	from;
u_long	fromport;
u_char	reserved[16];

Размер 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);

Вот как всё это безобразие выглядит:

""" MRIM constants """
PROTO_VERSION_MAJOR             = 1
PROTO_VERSION_MINOR             = 12 #7
PROTO_VERSION                   = (PROTO_VERSION_MAJOR << 16) | PROTO_VERSION_MINOR
CS_MAGIC                        = 0xDEADBEEF

MRIM_CS_HELLO                   = 0x1001  # C -> S
MRIM_CS_HELLO_ACK               = 0x1002  # S -> C
MRIM_CS_LOGIN_ACK               = 0x1004  # S -> C
MRIM_CS_LOGIN_REJ               = 0x1005  # S -> C
MRIM_CS_PING                    = 0x1006  # C -> S
MRIM_CS_MESSAGE                 = 0x1008  # C -> S
MESSAGE_FLAG_OFFLINE            = 0x00000001
MESSAGE_FLAG_NORECV             = 0x00000004
MESSAGE_FLAG_AUTHORIZE          = 0x00000008  # X-MRIM-Flags: 00000008
MESSAGE_FLAG_SYSTEM             = 0x00000040
MESSAGE_FLAG_RTF                = 0x00000080
MESSAGE_FLAG_CONTACT            = 0x00000200
MESSAGE_FLAG_NOTIFY             = 0x00000400
MESSAGE_FLAG_MULTICAST          = 0x00001000
MAX_MULTICAST_RECIPIENTS        = 50
MESSAGE_USERFLAGS_MASK          = 0x000036A8  # Flags that user is allowed to set himself
MRIM_CS_MESSAGE_ACK             = 0x1009  # S -> C
MRIM_CS_MESSAGE_RECV            = 0x1011  # C -> S
MRIM_CS_MESSAGE_STATUS          = 0x1012  # S -> C
MESSAGE_DELIVERED               = 0x0000  # Message delivered directly to user
MESSAGE_REJECTED_NOUSER         = 0x8001  # Message rejected - no such user
MESSAGE_REJECTED_INTERR         = 0x8003  # Internal server error
MESSAGE_REJECTED_LIMIT_EXCEEDED = 0x8004  # Offline messages limit exceeded
MESSAGE_REJECTED_TOO_LARGE      = 0x8005  # Message is too large
MESSAGE_REJECTED_DENY_OFFMSG    = 0x8006  # User does not accept offline messages
MRIM_CS_USER_STATUS             = 0x100F  # S -> C
STATUS_OFFLINE                  = 0x00000000
STATUS_ONLINE                   = 0x00000001
STATUS_AWAY                     = 0x00000002
STATUS_UNDETERMINATED           = 0x00000003
STATUS_FLAG_INVISIBLE           = 0x80000000
MRIM_CS_LOGOUT                  = 0x1013  # S -> C
LOGOUT_NO_RELOGIN_FLAG          = 0x0010  # Logout due to double login
MRIM_CS_CONNECTION_PARAMS       = 0x1014  # S -> C
MRIM_CS_USER_INFO               = 0x1015  # S -> C
MRIM_CS_ADD_CONTACT             = 0x1019  # C -> S
CONTACT_FLAG_REMOVED            = 0x00000001
CONTACT_FLAG_GROUP              = 0x00000002
CONTACT_FLAG_INVISIBLE          = 0x00000004
CONTACT_FLAG_VISIBLE            = 0x00000008
CONTACT_FLAG_IGNORE             = 0x00000010
CONTACT_FLAG_SHADOW             = 0x00000020
MRIM_CS_ADD_CONTACT_ACK         = 0x101A  # S -> C
CONTACT_OPER_SUCCESS            = 0x0000
CONTACT_OPER_ERROR              = 0x0001
CONTACT_OPER_INTERR             = 0x0002
CONTACT_OPER_NO_SUCH_USER       = 0x0003
CONTACT_OPER_INVALID_INFO       = 0x0004
CONTACT_OPER_USER_EXISTS        = 0x0005
CONTACT_OPER_GROUP_LIMIT        = 0x6
MRIM_CS_MODIFY_CONTACT          = 0x101B  # C -> S
MRIM_CS_MODIFY_CONTACT_ACK      = 0x101C  # S -> C
MRIM_CS_OFFLINE_MESSAGE_ACK     = 0x101D  # S -> C
MRIM_CS_DELETE_OFFLINE_MESSAGE  = 0x101E  # C -> S
MRIM_CS_AUTHORIZE               = 0x1020  # C -> S
MRIM_CS_AUTHORIZE_ACK           = 0x1021  # S -> C
MRIM_CS_CHANGE_STATUS           = 0x1022  # C -> S
MRIM_CS_GET_MPOP_SESSION        = 0x1024  # C -> S
MRIM_CS_MPOP_SESSION            = 0x1025  # S -> C
MRIM_GET_SESSION_FAIL           = 0
MRIM_GET_SESSION_SUCCESS        = 1
MRIM_CS_WP_REQUEST              = 0x1029  # C->S
PARAMS_NUMBER_LIMIT             = 50
PARAM_VALUE_LENGTH_LIMIT        = 64
MRIM_CS_ANKETA_INFO                = 0x1028  # S->C
MRIM_ANKETA_INFO_STATUS_OK         = 1
MRIM_ANKETA_INFO_STATUS_NOUSER     = 0
MRIM_ANKETA_INFO_STATUS_DBERR      = 2
MRIM_ANKETA_INFO_STATUS_RATELIMERR = 3
MRIM_CS_MAILBOX_STATUS             = 0x1033
MRIM_CS_CONTACT_LIST2              = 0x1037  # S->C
GET_CONTACTS_OK                    = 0x0000
GET_CONTACTS_ERROR                 = 0x0001
GET_CONTACTS_INTERR                = 0x0002
CONTACT_INTFLAG_NOT_AUTHORIZED     = 0x0001
MRIM_CS_LOGIN2                     = 0x1038  # C -> S
MAX_CLIENT_DESCRIPTION             = 256

class packet:
  """ MRIM handler """
  magic     = CS_MAGIC
  proto     = PROTO_VERSION
  seq       = 0x00000000
  msg       = 0x00000000
  dlen      = 0x00000000
  from_addr = 0x00000000
  from_port = 0x00000000
  reserved  = 0x00000000
  data      = ""
  size      = 44

  def __init__(self):
    self.reserved  = self.__d2s(0x00000000, 16)  # 16 байт

  def setHost(self, host, port):
    self.from_host, self.from_port = host, port
    return True

  def getPacket(self, msg):
    self.seq += 1
    self.msg  = msg
    self.dlen = len(self.data)
    return self.__d2s(self.magic, 4) + self.__d2s(self.proto, 4) + \
      self.__d2s(self.seq, 4) + self.__d2s(self.msg, 4) + \
      self.__d2s(self.dlen, 4) + self.__d2s(self.from_addr, 4) + \
      self.__d2s(self.from_port, 4) + self.reserved + self.data

  def setPacket(self, p):
    if len(p) < 44:
      return False
    self.t = p
    self.magic     = self.__s2d(p[   :  4])
    self.proto     = self.__s2d(p[4  :  8])
    self.seq       = self.__s2d(p[8  : 12])
    self.msg       = self.__s2d(p[12 : 16])
    self.dlen      = self.__s2d(p[16 : 20])
    self.from_addr = self.__s2d(p[20 : 24])
    self.from_port = self.__s2d(p[24 : 28])
    self.reserved  = self.__s2d(p[28 : 44])
    if len(p[44 : ]):
      self.data    = self.__s2d(p[44 : ])
    else:
      self.data    = ""
    return True

  def addRawData(self, data):
    self.data += data
    self.dlen = len(self.data)
    return True

  def addLPSData(self, data):
    dlen = len(data)
    self.data += self.__d2s(dlen, 4)
    self.data += data
    self.dlen = len(self.data)
    return True

  def addULData(self, data):
    self.data += self.__d2s(data, 4)
    return True

  def clear(self):
    self.data = ""
    self.dlen = ""
    self.msg = ""

  def __d2s(self, d, size = 4):
    d = hex(d)[2 : ]
    if d[-1] == "L":
      d = d[ : -1]
    ln = len(d)
    if ln % 2:
      ln += 1
      d = "0" + d
    i = 0
    r = ""
    while i < ln:
      r = chr(int(d[i : i + 2], 16)) + r
      i += 2
    while len(r) < size:
      r += chr(0x00)
    return r

  def __s2d(self, s):
    r = ""
    i = len(s)
    while i:
      t = hex(ord(s[i - 1: i]))[2 : ]
      if len(t) < 2: t = "0" + t
      r += t
      i -= 1
    return int(r, 16)

Теперь создадим файл 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 прошло больше суток;

Код у меня выглядит так:

import sqlalchemy as sa
from sqlalchemy.orm import mapper, sessionmaker
import time

engine = sa.create_engine("sqlite:///database.db", echo=False)
metadata = sa.MetaData()

emails_table = sa.Table("emails", metadata,
                        sa.Column("email", sa.String(convert_unicode=False), primary_key=True),
                        sa.Column("password", sa.String(convert_unicode=False), nullable=False),
                        sa.Column("limit", sa.Integer, nullable=False, default=50),
                        sa.Column("last_sms", sa.Integer, nullable=False, default=0)
                       )

metadata.create_all(engine)

class Email(object):
  def __init__(self, email, password, limit = 50, last_sms = 0):
    self.email      = email
    self.password   = password
    self.limit      = limit
    self.last_sms   = last_sms

  def __repr__(self):
    return "" % (self.email, self.password, self.limit, self.last_sms)

def add_emails(emails, session):
  try:
    femails = open(emails, "r")
  except:
    print "Error. Can't open file %s." % emails
    return False
  try:
    counter = 0
    for e in femails:
      login, password = e.strip("\r\n").split(" ", 1)
      c = session.query(Email).filter(Email.email == login).count()
      if not c:
        session.add(Email(login, password))
        counter += 1
    session.commit()
  except:
    femails.close()
    print "Error. Unknow exception :-(."
    return False
  femails.close()
  return counter

def update_limits(session):
  e = session.query(Email).filter(Email.last_sms <= int(time.mktime(time.localtime()) - 86400))
  for email in e:
    email.limit = 50
  session.commit()

mapper(Email, emails_table)
Session = sessionmaker(bind = engine)

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

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

import socket
import os
import urllib
from packets import *

class clnt:
  rhost = ""
  rport = 0
  sock = False
  ping = 0
  packet = False
  rcvd_packet = False

  def __init__(self, rhost, rport):
    self.rhost, self.rport = rhost, rport
    self.packet = p_header()
    self.rcvd_packet = p_header()

  def connect(self):
    # Создаём сокет и подключаемся
    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
      self.sock.connect((self.rhost, self.rport))
      addr, port = self.sock.getsockname()
      self.packet.setHost(addr, port)
    except:
      print "\nError. Connection to server %s:%d failed." % (self.rhost, self.rport)
      return False
    return True

  def start(self, login, passwd):
    if not self.connect(): return False
    if not self.hello(): return False
    if not self.auth(login, passwd): return False

    return True

  def hello(self):
    self.send(self.packet.getPacket(MRIM_CS_HELLO))
    #print "Sent MRIM_CS_HELLO."
    try:
      self.rcvd_packet.setPacket(self.get())
      if self.rcvd_packet.msg == MRIM_CS_HELLO_ACK:
        #print "Recieved MRIM_CS_HELLO_ACK.",
        self.ping = self.rcvd_packet.data
        #print "Ping timeout %ds." % self.ping
      else:
        print "\nError. Not recieved MRIM_CS_HELLO_ACK."
        return False
    except:
      print "\nError. Not recieved MRIM_CS_HELLO_ACK."
      return False
    return True

  def auth(self, login, passwd):
    self.packet.clear()
    self.packet.addLPSData(login)
    self.packet.addLPSData(passwd)
    self.packet.addULData(STATUS_ONLINE)
    self.packet.addLPSData("STATUS_ONLINE")
    self.packet.addLPSData("FreeAgent v 0.1")
    print "Trying to authorize as %s..." % login,
    try:
      self.send(self.packet.getPacket(MRIM_CS_LOGIN2))
    except:
      print "\nError. Can't send MRIM_CS_LOGIN2."
      return False
    try:
      self.rcvd_packet.setPacket(self.get())
    except:
      print "\nError. Not recievd answer from server."
      return False
    if self.rcvd_packet.msg == MRIM_CS_LOGIN_ACK:
      print "success!"
      return True
    else:
      print "error :-(."
      return False

  def sendSMS(self, number, text):
    ftext = ""
    ftext = text
    self.packet.clear()
    self.packet.addULData(0x0)
    self.packet.addLPSData(number)
    self.packet.addLPSData(ftext)
    print "Sending to %s: %s..." % (number, text),
    try:
      self.send(self.packet.getPacket(0x00001039))
    except:
      print "\nError. Can't send sms"
      return False
    print "Ok!"
    return True

  def send(self, msg):
    try:
      self.sock.send(msg)
    except:
      print "\nError. Can't send data."
      return False
    return True

  def get(self):
    return self.sock.recv(4096)

  def close(self):
    self.sock.close()
    return True

Все основные функции и классы написаны, осталось их только использовать. Создаём главный файл sms.py.
Первое, что мы в нём делаем — это «обновляем» лимиты в 50 сообщений в сутки, далее проверяем количество аргументов командной строки, если оно равно трём, то считаем, что нам переданы номер, на который надо отправить sms, и текст (т.е. программа была запущена так: sms.py +71234567890 «testovoe soobshenie»), иначе входим в интерактивный режим.
Следующий шаг — пытаемся подключиться к адресу «http://mrim.mail.ru:2042″ и узнать адрес сервера к которому нам рекомендуют подключаться на данный момент.
Выбираем из нашей БД список ящиков, у которых не превышен лимит, упорядочиваем (по возрастанию) по дате отправки последнего сообщения и отправляем sms с 1го, уменьшая лимит этого ящика на 1. Если со времени последней отправки sms не прошла ещё 61 секунда, то перед отправкой ждём.

# -*- coding: cp1251 -*-

from clnt import *
import sys
import time
import urllib
from emails import *
import os

session = Session()
update_limits(session)

if len(sys.argv) == 3:
  number = sys.argv[1]
  text = sys.argv[2]
else:
  while True:
    action = raw_input("Select action:\n1 - add emails from file\n2 - send sms\n")
    if action == "1":
      ef = raw_input("Enter file name: ")
      print "%d new emails added." % add_emails(ef, session)
    elif action == "2":
      number = raw_input("Enter phone number: ")
      text = raw_input("Enter SMS text: ")
      break
    else:
      print "Invalid action!"

try:
  saddr = urllib.urlopen("http://mrim.mail.ru:2042").readlines()
  saddr = saddr[0]
  shost, sport = saddr[:-1].split(":")
  sport = int(sport)
except:
  print "\nError. Can't connect to mrim.mail.ru."
  sys.exit(0)

emails = session.query(Email).filter(Email.limit > 0).order_by(Email.last_sms)
if not emails.count():
  print "Sorry, all limits exceeded."
  sys.exit(0)
if emails[0].last_sms > (int(time.mktime(time.localtime()) - 61)):
  print "Waiting %d seconds." % (61 - (int(time.mktime(time.localtime())) - emails[0].last_sms))
  time.sleep(61 - (int(time.mktime(time.localtime())) - emails[0].last_sms))
c = clnt(shost, sport)
if not c.start(str(emails[0].email), str(emails[0].password)):
  print "Error."
  sys.exit(0)
c.sendSMS(number, text)
emails[0].last_sms = int(time.mktime(time.localtime()))
emails[0].limit -= 1
session.commit()
c.close()
session.commit()

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

Ссылки к статье:
http://agent.mail.ru/ru/developers/protocol.html — описание протокола агента.
http://sqlalchemy.org — официальный сайт sqlalchemy.
http://www.arechisoft.com/ — пакетный снифер EtherSnoop.
http://yo.jabber.ru/files/mrim/ — Mrim — это Jabber-транспорт для IM-сервиса компании Mail.Ru написанный на python.

Программирование, сетевое , , , , ,

Пожалуйста, оцените полезность и качество данной статьи. Одна звезда - плохо, 5 - хорошо.
1 звезда2 звезды3 звезды4 звезды5 звёзд (6 голосов, средний: 5,00 из 5)
Loading ... Loading ...

  1. topinambur
    16 Декабрь 2008 в 08:26 | #1

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

  2. lizz
    16 Декабрь 2008 в 12:49 | #2

    Удачи, будем рады, если поедлитесь наработками, может кому ещё пригодятся ;-).

  3. ristezze
    5 Январь 2010 в 23:37 | #3

    Что-то SMSки не доходят… Еще ни один скрипт не пашет, что только не пробывал.

  4. 8 Январь 2010 в 15:52 | #4

    Тут может быть несколько проблем.
    1. Оператор точно есть в списке поддерживаемых?
    2. Лимит на мыле на количество смс не исчерпан?
    3. Дата поста 18 Ноябрь 2008 :) Делайте выводы о свежести версии протокола. На оф сайте кстати про отправку смс ничего не было указано, да и на то время использовался более новый протокол. В общем качайте сниффер, проверяйте формат пакетов агента и этой реализации.

    Если возникнут какие вопросы — будем рады услышать ;). Первые 2 пункта советую проверить через официальный клиент маилру.

  5. ristezze
    11 Январь 2010 в 15:24 | #5

    Первые 2 пункта проверил сразу, там все нормально:)
    Сниффать пробовал. Отлавливал пакеты, которые на 2042 и 443 порт уходят. Вот только пакеты идут на эти порты только во время авторизации, а дальше глухо, как в танке.
    Что еще можно сделать? Заранее спасибо.

  6. 17 Январь 2010 в 12:21 | #6

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

    Кстати, а что на 443 порту? http://mrim.mail.ru:2042 — по этому адресу мы получаем адрес сервера к которому надо коннектиться только, можешь через браузер зайти по нему и глянуть. Мне выдал 94.100.179.31:2041, следовательно смотреть надо что идёт на этот адрес и порт.

  7. ristezze
    18 Январь 2010 в 21:42 | #7

    Дык какой смысл мне было скрипт то сниффать? Конечно же агент! Весь траффик не интересно как-то:) Я wireshark использовал, там фильтры можно нормальные ставить. Вот теперь то я и понял в чем ошибка была! Порт 2042 используется только для получения адреса, а дальше уже 2041 идет! Как-то недоглядел я это… Смотреть что идет на адрес 94.100.179.31 смысла совсем никакого нет, так их сервак всегда разный АйПишник выдает, сейчас пойду гляну, что с 2041 порта уходит! Спасибо за наводку!

  8. 23 Январь 2010 в 21:57 | #8

    На водку, дайте мне на водку! :D Так, вспомнилось что-то). Всегда пожалуйста, если будет желание переписать — можешь потом поделиться с народом исходниками через нас (или не через нас ;)).

    Адрес… ну да, разный, раньше вроде не каждый раз менялся, если мне не изменяет память, но прогресс не стоит на месте :).

  9. ristezze
    24 Январь 2010 в 22:01 | #9

    И на пиво сраками! Эх, я дамп пакетов сохранил, как будет время, то скрипты перепишу и выложу:) Вот только не знаю когда.

  10. 1 Февраль 2010 в 12:22 | #10

    Мы не спешим. Вы видимо тоже :).

  11. dimasbka
    18 Май 2010 в 17:25 | #11

    Как прогресс? Пока не работает?
    Такая фишка будет полезна чтобы работу сайта контролировать ))
    Хочется, но к сожалению самому разобраться возможности нет: питоном только осваиваю, а как пакеты разбирать вообще темный лес

  12. 19 Май 2010 в 21:05 | #12

    @dimasbka
    Если очень интересно, то могу написать этому человеку на мыло, если конечно он указал правильный email. Сам не занимался, но уверяю, тут ничего сложного нету кроме, быть может, моей реализации… сейчас я всё переписал бы несколько по другому))). Увы, у самого времени не хватает на всё %). Для упрощения можно выкинуть sqlalchemy, и реализовать только нужный функционал.

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

  13. ristezze
    26 Июль 2010 в 23:33 | #13

    Эх, блин, так руки и не дошли. Тогда надо было, а сейчас уже нет. Ненадежно все это, хоть и на шару:)

  14. 27 Июль 2010 в 17:46 | #14

    Бывает, сам таким страдаю :). Ненадёжно это да… теперь вот плотнее с mail.ru работаю по роду деятельности, по-моему у них ничего надёжного нет, постоянно что-то отваливается, не работает. Хотя может этим все страдают :).

  15. ristezze
    28 Июль 2010 в 12:47 | #15

    Это точно! Помню у них почтовый сервак упал, причем все письма сгорели синим пламенем, хорошо, что не мой ящик был:)

  16. Dominic
    17 Февраль 2011 в 00:57 | #16

    Так заработало у кого нибудь или нет?

  1. Пока что нет уведомлений.
*