сохранение jabber-статусов в atom

три года назад я решил, что лучший микроблог — это статус в джаббере. Правда, у него был один недостаток: его было нетривиально вставлять в мой RSS-поток рекомендаций. Эта проблема легко решалась сторонним сервисом — у френдфида был джаббер-бот, который мог отправлять статусы во френдфид, а оттуда уже можно было вынуть RSS.

к сожалению, фейсбук купил команду френдфида, и тот потихоньку начал разлагаться. Бот не работает уже давно. Впрочем, технически задача очень проста: подключиться к серверу, ждать обновлений статуса, сохранять их в поток. К сожалению, на моём сервере оказываются только LTS-версии убунты, и предыдущая версия 10.04 не имела какой-то из нужных мне библиотек на питоне. Зато недавно я сервер обновил, и вскоре после этого смог завершить и запустить своего бота. Теперь мои статусы оказываются в специальном потоке.

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

class Client(JabberClient):
    def __init__(self):
        JabberClient.__init__(self,
            JID(JID_VALUE),
            PASSWORD,
            server='talk.google.com',
            auth_methods=['plain'],
            tls_settings=TLSSettings(require=True, verify_peer=False))

client = Client()
client.connect()
client.loop(1)

настроек у клиента могло бы быть и поменьше, но Google Talk очень особенный. Зато дальше становится проще. Вот так подключается обработчик событий:

self.get_stream().set_presence_handler('available', self.presence)

а вот, собственно, и сам обработчик:

def presence(self, stanza):
    status = stanza.get_status()

    if stanza.get_show() is not None or not status or status == self.current_status:
        return True

    self.current_status = status
    self.update_feed(status)
    publish(PUSH_URL, SELF_URL)

    syslog.syslog('New status: %s' % (status.encode('utf8')))

    return True

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

кстати, мне хотелось, чтобы этот бот был полноценным сервисом на моём сервисе. Для этого пришлось разобрать основы системы upstart, которая призвана заменить предыдущего управляющего сервисами init. Оказалось, с новой системой простые вещи действительно просты. Вот такой файл я добавил в /etc/init:

description "Logger of IM statuses"
author  "Artemy Tregubenko <me@arty.name>"

start on runlevel [234]
stop on runlevel [0156]

expect daemon
respawn

chdir /var/www/shared_arty_name/im-status/
exec python /var/www/shared_arty_name/im-status/IMStatus.py

после этого оставалось только научить программу на питоне вести себя, как демон, это тоже было довольно просто:

def start():
    client = Client()
    client.connect()
    client.loop(1)

def stop():
    client.disconnect()

context = daemon.DaemonContext(
    signal_map={signal.SIGTERM: stop},
    gid=grp.getgrnam('www-data').gr_gid,
    uid=grp.getgrnam('www-data').gr_gid,
    detach_process=True,
)

with context: start()

а вот и код бота целиком. Интересно, есть ли смысл превращать его в сервис? Глядишь, кто-нибудь на пиво и пожертвует : )

Артемий Трегубенко,
, , ,

comments powered by Disqus