сохранение 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()
а вот и код бота целиком. Интересно, есть ли смысл превращать его в сервис? Глядишь, кто-нибудь на пиво и пожертвует : )