Журнал LinuxFormat - перейти на главную

LXF121:Python

Материал из Linuxformat
Перейти к: навигация, поиск
Python Заставим Web доставлять нужное содержимое вам на блюдечке

Содержание

Python: Создаем говорящего бота

Часть 2: Думаете, жизнь может быть лучше? Она и станет лучше, если вы, следуя Нику Вейчу, создадите себе бота – чтоб вкалывал за вас.

Представьте, как упростилась бы наша жизнь при наличии оравы слуг, готовых выполнять наши приказы: они приносили бы почту, чистили клетку с гиппогрифом, пинали Майка… ну, всякое такое. Увы, мы живем в слишком раннем историческом периоде, когда еще не внедрены дешевые, эффективные и послушные роботы-слуги, и приходится все делать самим. Другой способ отлынивать от работы – завести виртуальную прислугу. О да, под это определение может попасть практически любое ПО, но я-то имею в виду простой и удобный интерфейс, способный выполнять полезные задачи и сообщать вам то, что вы хотите знать. Если он сумеет ставить в тупик ваших друзей и убеждать ваших врагов пересмотреть свою матрицу враждебности, тем лучше.

Нечасто используемым, но удобным способом общения с подобными существами является чат. Стоит ли связываться с хитроумными SSH-туннелями или нудными сервисами на базе web, если можно легко общаться с помощью средства, из которого вы и так не вылезаете? В свете вышесказанного, наш маленький подручный будет чат-ботом: пусть сидит на канале чата и ждет, когда хозяин подаст голос; или, допустим, при случае проявляется и сообщает вам о чем-нибудь.

Я Xmpppy

Протокол Jabber/XMPP отлично поддерживается Python. Он реализован в рамках всеобъемлющего сетевого модуля Twisted, но есть и более легковесная версия, подходящая для наших нужд, по имени Xmpppy. В вашем дистрибутиве наверняка имеется этот пакет, но можно скачать код Python с сайта http://xmpppy.sourceforge.net.

Чтобы разобраться в Xmpppy, начнем с примеров командной строки. Но сперва обзаведемся тестовой учетной записью: разве ваш бот не заслужил собственного имени пользователя в Google? Для тестирования вам понадобятся как минимум два идентификатора Jabber ID. Итак, мы зарегистрировали учетную запись Gmail специально для бота, вошли через web-браузер и пригласили другого пользователя Gmail в чат. Можно было бы настроить все это в самом Xmpppy, но так будет сложнее: для первичного тестирования лучше иметь две учетных записи, которые могут общаться друг с другом. После этого наберите в оболочке python для доступа к интерактивной командной строке Python:

 >>> import xmpp
 >>> jid=xmpp.protocol.JID(“botaddress@gmail.com)

Это мы установили ID Jabber. Он сильно смахивает на адрес электронной почты, потому что мы для целей нашего урока взяли учетную запись Google; однако любой другой Jabber ID тоже подойдет. Он состоит из двух частей (а может и из трех, но об этом потом): имя пользователя и домен. Домен – это место, где Xmpppy будет искать сервер.

Создав пользователя, надо создать и экземпляр клиента. Клиент в Xmpppy – это объект, который контролирует соединение, обрабатывает сообщения и вообще взаимодействует с сервером. Клиент и его подключение создаются за несколько шагов: сначала нужно создать экземпляр клиента (в качестве аргумента потребуется Jabber ID), а затем мы попробуем соединиться с сервером. Установив соединение, нужно аутентифицироваться для выполнение дальнейших действий.


 >>> myclient=xmpp.Client(jid.getDomain(),debug=[])
 >>> myclient.connect()
 ‘tls’
 >>> myclient.auth(jid.getNode(),’botpasswd’)
 ‘sasl’

Здесь нужно обратить внимание на пару моментов. Сперва мы создали экземпляр клиента и вызвали метод jid.getDomain для получения имени сервера из созданного нами объекта jid. Далее, вас просят еще указать уровень отладки, определяющий качество обратной связи, которую вы получаете. Мы установили свой на пустой список, но если вам охота завалить себе экран сообщениями, добавьте в него строку always. Впрочем, при наличии проблем сообщения могут и пригодиться.

Даешь Disco

XMPP включает структуру модулей расширения под названием Disco. Они обогащают протокол другими типами сообщений, включая SIP (голосовые) и передачу файлов, и всеми мыслимыми возможностями, для которых применяется сеть «точка–точка». Так почему бы не прибавить функциональности и нашему боту – например, превратить его в хранилище файлов или чтеца новостей?

К счастью, Google расширил свою реализацию XMPP некоторыми дополнительными функциями, подробную информацию о которых вы найдете на сайте http://code.google.com/apis/talk/jep_extensions/extensions.html.

Создание соединения

После установки уровня отладки мы создали соединение (пароль тут подставьте ваш собственный) и получили ответный результат tls, который означает, что с сервером установлено безопасное соединение – вы можете также увидеть tcp для стандартного сетевого соединения, или пустую строку, если в соединении было отказано.

При малой прыткости ваших пальцев и в зависимости от вашего сервера, следующий шаг может не сработать: серверы Google ожидают от вас быстрой аутентификации соединения, в противном случае оно оборвется, оставив вас скрести в затылке и раздумывать, что за беда тут стряслась. Вам дается всего несколько секунд, и вы, возможно, захотите объединить следующие строки вместе:

 >>> myclient.connect() ; myclient.auth(jid.getNode(),’botpasswd’)
 ‘tls’
 ‘sasl’

Результат sasl говорито том, что соединение аутентифицировано с использованием SASL (Simple Authentification and Security Layer, Простая аутентификация и слой безопасности) посредством вашего пароля. Последний шаг установки соединения – объявление вашего статуса. Как вы знаете, в Google Chat и других сервисах Jabber существует несколько различных уровней статусов клиентов (например, Доступен или Занят). Кроме всего прочего, они используются сервером для составления списков доступных контактов, а в Xmpppy для этого существует специальный метод:

 myclient.sendInitPresence()

Учтите, что некоторые серверы не дадут вам и шагу ступить, пока вы не укажете это правильным образом.

Послание в бутылке Ура, есть контакт! Теперь, прежде чем заняться скучными вещами, давайте составим и отправим сообщение нашему оппоненту (хороший кандидат – ваш собственный адрес Google). Однако сначала нужно обзавестись адресом, куда требуется доставить сообщение, и его текстом. После этого создадим сообщение и отправим его

 mymsg=xmpp.protocol.Message(“evilbot.1@gmail.com”,”hello”, “chat”)
 myclient.send(mymsg)

Если вы справились с приглашением и разрешением на чат от учетной записи вашего бота, вы должны получить приятный сюрприз. Одна штука может вас озадачить: а как установить свое статусное сообщение? Сервер отображает только статус и соответ-ствующее сообщение, переданные клиентом.

На сервере Google это означает, что вы можете иметь статус Не на месте, Невидим, Отсутствую, Не беспокоить или На месте, а также в форме текстового сообщения. А вот и разгадка: ваш статус задается отправкой специального сообщения XMPP, которое идет прямо на сервер. Оно включает ваш статус и текст статусного сообщения. Не зная этого, самому догадаться сложно, но смысл тут есть: XMPP как раз предназначен для отправки сообщений, так зачем же заводить отдельные протоколы только для отправки сообщений про статус? Сервер отвечает за обновление статуса для всех, кто соединен с вами. Кстати говоря, протокол XMPP позволяет отправлять контактам явные сообщения о статусе, но, хотя тут и есть с чем порезвиться, обычно мы перекладываем эту заботу на сервер:

 >>> presence = xmpp.Presence(status = “Ready!”, show = “chat”, priority = ‘1)
 >>> myclient.send(presence)

Модуль Xmpppy работает с сообщениями о статусе иначе, чем с обычными, потому что они используют другие аргументы, но механизм отправки тот же самый.


Прослушивание

От хорошего бота также нужно получать входящие сообщения. Среда Xmpppy принимает весточки, отправленные на созданный вами клиентский объект, и сохраняет их в стеке для обработки. А какой? Xmpppy использует концепцию обработчиков [handler]: сперва вы определяете функцию или метод, который будет действовать как получатель информации. Затем, при готовности обработать сообщения из стека, вы просто вызываете метод Process() объекта клиента. Звучит это сложнее, чем есть на самом деле – другими словами, вы сообщаете клиенту, где нарыть сообщений, затем велите ему пропустить их через функцию. Конечно, это можно сделать и через командную строку Python, но тогда будет сложнее: нужно написать функцию обработчика, а затем, обычным порядком, запустить бесконечный цикл для обработки сообщений по мере их прибытия.

Мы покажем соответствующий код в командной строке, но разумнее будет встроить его в собственный класс (как мы увидим далее). В данном примере, мы также отправим сообщение самим себе для тестирования, а вы можете сделать это через Gmail:

 >>> def msgparser(instance, message):
 ... printnew message!”
 ... printfrom: “ + str(message.getFrom())
 ... print “msg: “ + str(message.getBody())
 ...
 >>> myclient.RegisterHandler(‘message’, msgparser)
 >>> mymsg=xmpp.protocol.Message(“evilbot@gmail.com”,”hello”, “chat”)
 >>> myclient.send(mymsg) 
 ‘5’
 >>> myclient.Process(1)
 new message!
 from: evilbot@gmail.com
 msg: hello
 1493
 >>>

Как видите, анализирующая функция msgparser() проста. Она использует доступные методы getFrom() и getBody() во входящем сообщении, преобразует результаты в строку и выдает на консоль. Для реального бота надо еще записать отправителя в переменную (чтобы ему отвечать), а может быть, разобрать сообщение глубже, чтобы сгенерировать ответ.

Помощь по Python

Если вы новичок в Python, но имеете опыт программирования на других языках, трудностей у вас возникнуть не должно: главное – помните, что надо правильно делать отступы. На основном сайте Python имеется масса документации по особенностям языка и синтаксису, а также по использованию стандартных модулей, поставляемых вместе с Python.

Командный голос

Для нашего простенького бота мы примем такой синтаксис: специальные команды будут начинаться с символа #. Получив сообщение, начинающееся с #, мы попытаемся сделать что-нибудь; а в противном случае вернем произвольный ответ из припасенного списка.

Добавление обработчика команд сделало бы код громоздким. Ради экономии места и умственных усилий применим трюк: используя конструкцию eval языка Python, мы создадим функцию, вызывающую метод в нашем классе с тем же именем, что и команда, которая была нам передана. Это, конечно, отчасти халтура, но экономит место и облегчает добавление команд – вам останется только определить новый метод. Для бота более продвинутого вы, возможно, захотите вставить механизм обработчиков, чтобы к экземплярам класса можно было добавлять свои команды, но ниже показано, как работает метод попроще (заметим, что данный код – это метод класса)


 def messageparse(self, instance, message): 
  m = message.getBody()
  sender=message.getFrom()
  if m[0]==’#’:
   self.log(‘command received’)
   #специальный случай
   #просто команда – подразумеваем, что метод существует, как и id отправителя
   try:
    getattr(self, m[1:])(sender)
   except:
    self.client.send((xmpp.protocol.Message(sender, ‘Sorry Dave, I can\’t do that...’)))
  else:
  #полагаю, надо сказать что-нибудь, из вежливости
   self.client.send((xmpp.protocol.Message(sender, random.choice(self.responses))))

Как видите, сообщение проверяется на наличие символа #, и если он есть, мы создаем вызов метода по остатку строки, включая ID отправителя, и пытаемся выполнить его. Конструкции try и execept отлавливают исключения – например, несуществующий метод. Если команды нет, мы просто отправляем случайный ответ, выбрав его из списка responses. На самом деле, для этого нужно импортировать модуль random; random.choice случайным образом выбирает элемент из тех, что ему подсунули.

Обработчик команд может быть таким:

 def uptime(self, sender):
  import subprocess
  p=subprocess.Popen([“uptime”], stdout=subprocess.PIPE)
  r=p.communicate()[0]
  self.client.send((xmpp.protocol.Message(sender,r)))

Объясним его, потому что он работает с запуском команд локально, на компьютере, где выполняется бот. Сначала идет определение, которое принимает экземпляр объекта self (это требование Python) и информацию об отправителе сообщения, добытую обработчиком. А потом мы импортируем subprocess из стандартных библиотек Python, для запуска локальной команды.

Кроме того, мы использовали метод под названием Popen (см. строку, начинающуюся с p=subprocess.Popen), который задокументирован и объяснен по адресу http://docs.python.org/library/subprocess.html. Вкратце, мы передаем команду для выполнения и требуем, чтобы стандартный вывод отсылался в определенное место. Тогда мы можем связаться с этим выводом, используя метод communicate экземпляра Popen, и получить результат команды. Последняя строка запаковывает его и отправляет в виде чат-сообщения. Теперь вы можете делать запросы к вашему серверу через чат-клиент и заставлять его злодействовать!

Идем дальше

Полный код с комментариями для класса нашего чат-бота включен на LFXDVD. Если вы собираетесь запустить его, подставьте данные учетной записи вашего бота. А если вы импортируете его в качестве модуля, можете ввести данные об учетной записи и пароле просто через конструктор. Код нашего примера должен предоставить вам достаточно информации, поясняющей, как он работает.

Возможно, вам захочется добавить к классу собственные методы для задания других команд: автоматизировать какие-нибудь задания или обратиться к внешним ресурсам – например, создать систему напоминаний, использующую онлайн-календарь. Или, почему бы не приткнуть вашего чат-бота к сервису переводов – включить его в беседу и заставить переводить все, что вы скажете, на другой язык?

Чат-бот – всего лишь перевалочный пункт: легкий в использовании интерфейс, позволяющий обращаться к скрипту. Как вы трудоустроите своего чат-бота, зависит от вас, от того, на что вы его запрограммируете. Если придумаете другие способы его применения, пожалуйста, напишите нам; а если вы чего-то недопоняли – не забудьте просмотреть полный код на DVD. LXF

Персональные инструменты
купить
подписаться
Яндекс.Метрика