Моя стаття з січневого випуску Хакера про wave. Хоча зараз ажіотаж трішки з Хвилі зійшов, але гадаю цей сервій ще буде розвиватися.
Ручной Wave Робот
Написание робота на Python'е для Google Wave
"Ладно, я построю свой собственный модуль с блек-джеком и шлюхами. Вообще-то, к черту модуль и блек-джека"... Какие хорошие советы могут давать роботы. Посему в статье построим и себе маленького робота, ну хотя бы лишь для Google Wave.
Google Wave получил хороший пиар, и думаю в сим мире не сыскать того, кто не слышал о нем (не считая, соседа Толика, который в запое;)). Хотя этот пиар может даже немножко повредил Волне, поскольку привозносил сие творение как убийца почты, форумов, и чуть ли не всего интернета. Как для меня же, Волна это достаточно классно сделанная IRC с кульным API. С помощью которого мы можем создавать любые гаджеты и, что более интересно, любых роботов, которые могут расширять возможности Wave под любые нужды. И наличие этого API поднимает планку Волне очень высоко. Так что с нашей стороны было бы огромнейшей ошибкой не рассказать тебе о программировании Волны. А мы же почти идеальны и не допускаем ошибок ;)
Робот API
Робот для Wave представляет e-mail с аваторкой, некоторым описанием, а также закрепленными за ним событиями. Событий где-то около 15 штук, но для большинства случаев хватает двух:WAVELET_SELF_ADDED
BLIP_SUBMITTED
Первое событие возникает когда робота добавляем на какую-то волну. А второе событие когда кто-либо добавляет сообщение, причем это сообщение возникает в момент нажатия на кнопку "Done". Гугл при программировании Волны вводит несколько понятий:
- wave - полностью весь Wave;
- wavelet - обозначает конкретную волну;
- blip - одно сообщение.
Помнишь в позапрошлом номере я тебе рассказывал о Google App Engine(GAE), так вот на данный момент роботов можно строить лишь с использованием GAE.
К практике!
Теория в случаях с Гуглом сложнее чем практика, посему не будем на ней задерживаться и перейдем к созданию робота. Все проекты на GAE начинаются с пустой папки и файла app.yaml, в котором установим что все запросы от wave будет обрабатывать скрипт jbot.py:
handlers:
- url: /_wave/.*
script: jbot.py
- url: /assets
static_dir: assets
Давай теперь определим ТТХ нашего робота... Пусть он должен уведомлять нас по джаберу о новых сообщениях в волну. И соответственно мы должны иметь возможность подписать свой джабер на обновления, и удалиться из рассылки. В тексте сообщения, что будет приходить, будет емайл автора и текст нового сообщения. Нам нужно перехватывать событие WAVELET_SELF_ADDED для вывода справки по командах, и будем перехватывать BLIP_SUBMITTED, чтобы рассылать уведомление.
Начнем прогить файл робота jbot.py и поместим сперва импорт нужных либ:
from waveapi import events
from waveapi import robot
Создадим обьект робота передав название, адрес аваторки, версию и адрес сайта робота. Заметь, что версию нужно менять обязательно если изменяется список событий, потому что новые события в этом случае не будут работать.
myRobot = robot.Robot('w-mailrobot',
image_url='http://w-mailrobot.appspot.com/images/avatar.png',
version='1',
profile_url='http://w-mailrobot.appspot.com/')
Теперь добавим перехват нужных событий и сам запуск робота:
myRobot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
myRobot.RegisterHandler(events.BLIP_SUBMITTED, OnBlipSubmitted)
myRobot.Run()
В регистрации перехватчиков мы указывали OnRobotAdded, OnBlipSubmitted - это названия функций, которых будут вызываться при событии. Они должны принимать 2 параметра - properties, context.
В properties информация относительно конкретного события. А в context - информация о окружении, о волне, где событие возникло. Именно через работу с context мы можем добавить новое сообщение в волну, вызвав такую длинную цепочку функций:
context.GetRootWavelet().CreateBlip().GetDocument().SetText(string)
Также с нее можем достать идентификатор волны, который нам немножко позже понадобиться:
waveid = context.GetRootWavelet().GetWaveId()
Теперь давай обработаем событие добавления робота на волну. И при получении данного события, мы добавим текстовое сообщение в волну с описанием команд, что бот принимает:
def addBlip(context, string):
context.GetRootWavelet().CreateBlip().GetDocument().SetText(string)
def OnRobotAdded(properties, context):
addBlip(context,"I'm alive!\n
Command:\n
wabber-bot add me: jabber@jabber.ja\n
wabber-bot remove me: jabber@jabber.ja")
Как видим мы добавили для будущего удобства функцию по добавлению сообщений - addBlip. А потом уже отправили сообщение.
Джабер
Чтобы продолжить нам уже следует научиться отсылать из GAE сообщения на джабер. Для этого активируем данный функционал - добавив в файл настроек app.yaml несколько строчек:
inbound_services:
- xmpp_message
Теперь можем юзать функции для отправки запроса авторизации и самих сообщений:
from google.appengine.api.xmpp import send_message, send_invite
#просьба авторизации
send_invite("кому")
#отослать сообщение
status = send_message("кому", "текст сообщения")
При програминге Google Wave иногда не очень понятно почему что-то не работает. Поэтому нам почти обязательно нужно использовать модуль logging. В GAE после его импортирования можем добавлять сообщения с разной степенью важности:
logging.info('info')
logging.error('error')
Обработчик этих сообщений автоматически добавить информацию о них в базу данных. И мы зайдя в админку можем их просматривать.
Был Blip?
Для завершения робота остался лишь последний шаг - написать OnBlipSubmitted - обработчик события о новом сообщении. В этой функции может быть 3 разные ситуации:
- была команда "добавить джабер в лист оповещения";
- была команда "удалить джабер с листа оповещения";
- небыло команды, тогда разослать это сообщения по подписчикам.
Если все это выразить на Python'e то получиться такое:
def OnBlipSubmitted(properties, context):
blip = context.GetBlipById(properties['blipId'])
text_blip = blip.GetDocument().GetText()
if text_blip.startswith('wabber-bot add me:'):
#добавить юзера
return
if text_blip.startswith('wabber-bot remove me:'):
#удалить юзера
return
#разослать всем сообщение
База данных
Для сохранения привязки джабер-акка к волне нам нужна база с двумя полями, одним для идентификатора волны, и один для джабера:
from google.appengine.ext import db
class WaveModel(db.Model):
wave = db.StringProperty()
user = db.StringProperty()
Теперь все готово к непосредственной обработке команд и добавлению пользователей в БД:
if text_blip.startswith('wabber-bot add me:'):
creator = text_blip[18:].strip()
count = WaveModel.all().filter('wave = ', waveid).filter('user =', creator).count()
if count:
return
WaveModel(
wave = waveid,
user = creator
).put()
send_invite(creator)
addBlip(context, "%s was added"%creator)
return
Как видим, мы не только добавляем юзера в базу данных, а еще и присылаем запрос авторизации, чтобы дальше все сообщения безпрепятственно доходили.
Принцип удаление пользователя из рассылки такой же самый, даже немного проще. Там по сути лишь 3 строчки главные:
rez = WaveModel.all().filter('wave = ', waveid).filter('user =', creator)
for i in rez:
i.delete()
Если же никакой команды нет, то достаем с базы все кто подписался и отправляем им сообщение:
all_u = WaveModel.all().filter('wave = ', waveid)
for u in all_u:
status = send_message(u.user, "New message from %s:\n%s"%(blip_creator, text_blip))
Вот так долго мы готовились к обработке команд, а кода получилось несколько строчек. Всего же робота в "собранном" виде смотри на диске.
Люди или Бендер?
Я после написания робота, залил его на GAE и уже можешь добавлять wabber-robot@appspot.com к себе в контакты и юзать. Хотя по идее нужно еще добавить подтверждения для джабера, чтобы только пользователь мог себя добавить в список. А то так получается, что любой джабер аккаунт можно заспамить. Но может это совсем и не бага, а фича ;)
Этой статье я не только завершаю цикл о Гугле, но и немного приостанавливаю цикла о Питоне. Мы ведь уже год разбираем многие вещи в нем и думаю ты стал реальным профи... Так что в следующем номере будем пробовать юзать что-то другое... к примеру Erlang.
Ну, Фрай, было приятно познакомиться, пойду, убью себя (с) БЕНДЕР
