Робот для администратора. Программим крутой jabber-бот с поддержкой плагинов на Python'еРоман «Spirit» Хоменко (http://tutamc.com) Британские ученые давно доказали, что собственным джаббер-ботом должен обладать любой уважающий себя администратор. Еще бы, ведь это он спасет в экстремальной ситуации, когда под рукой нет putty, зато есть джаббер-клиент (ты ведь уже завязал с аськой, правда?).
Пришло тепло, а вместе с ним - и выезды на природу с шашлыками. И пока я смотрел на костер, предвкушая начало отдыха, зазвонил телефон (в соответствии с приказами он должен быть при каждом сотруднике милиции на случай тревоги или команды «сбор»). Звонил полковник, срочно вызывающий меня на работу и требующий выдать ему на серваке новый пароль. Работы на пару минут, но где же я в лесу достану комп с инетом? Матерый гик портативную ЭВМ себе, наверняка, найдет и там (пить, гулять и морально разлагаться без ноута/субноута немыслимо!), но я оказался не таков, и на этом пикник для меня закончился. Вечером я вспомнил, что на телефоне у меня установлен джаббер-месенджер, и, если бы на серваке присутствовал соответствующий бот, который мог бы исполнять его команды, летний отдых бы не обломался. «Никогда не поздно начать», - сказал себе я и принялся за работу. Python & JabberПочему Python? А потому, что зеленый змий - единственный развивающийся (в отличие от Perl'а, развитие которого де-факто прекращено) системный (в отличие от вебовского PHP) скриптовый язык программирования. Выбор протокола сделать еще проще, ведь у джаббера в этом плане нет конкурентов - аська не подходит ввиду отсутствия нативного шифрования и централизованности (их сервак может лежать или капиталистическое начальство опять начнет что-то мутить со сменой протокола). Юзать на Питоне джаббер-протокол можно двумя способами: простым и сложным. «Сложно» - это путем чтения документации (xmpp.org/rfcs/) и с использованием сокетов. Если хочешь разобраться с протоколом – это способ оптимален, советую посмотреть, как сделан бот от eLWAux (исходники есть на диске). Легкий путь подразумевает использование уже написанных библиотек. Их немало, например:
Выбор определяется в основном личными пристрастиями. Мне больше понравилась xmppppy от Алексея Нежданова. Ею мы и воспользуемся. АрхитектураИтак, задача поставлена: написать джаббер-бот, способный принимать и исполнять команды. Нет, стой, это слишком просто. Усложним все и напишем полноценный бот с поддержкой плагинов, один из которых и будет решать задачу администрирования. Система у нас будет состоять из бота (bot.py), файла конфигурации (config.ini), папки с плагинами (plugins) и библиотеки xmpppy. Бот будет иметь два типа плагинов. Представители первой группы будут доступны всем, второй – лишь избранным, по списку или паролю. При проектировании системы с плагинами всегда встает вопрос об их полномочиях по управлению ботом. С одной стороны, чем меньше прав плагину мы даем, тем проще их писать, но и возможности получаются более ограниченными. Поэтому пусть лучше плагины будут низкоуровневыми. Трудностей мы не боимся. Система конфиговПрограмминг бота начнем с реализации чтения конфига, который будет храниться во всем известном ini-формате в файле config.ini (кстати, амеры весело произносят «ini» как «айнай»). В секции connect мы разместим параметры доступа к аккаунту, на котором будет висеть бот, а в секции permission - список юзеров, имеющих доступ к админке (пароли будут храниться там же). Удобную работу с ini-файлами в Питоне обеспечивает библиотека ConfigParser. Для чтения параметров мы используем две функции оттуда. Первая, read - для чтения конфигурационного файла, имя которого передается как параметр. Вторая функция, get, нужна, чтобы достать какой-то параметр. Она принимает в качестве параметров секцию и имя параметра. Рассмотрим эту функцию:
Здесь мы читаем переменную со списком юзеров, которым разрешен доступ в админку и превращаем в список методом split по запятой как разделителю. Далее все параметры мы возвращаем упакованными в ассоциативный массив (на языке Питона - словарь). Теперь конфиг можно прочитать:
Запуск ботаПерейдем к использованию xmpppy и запуску бота. Сперва создадим объект jid от xmpp.JID, передав имя пользователя, взятого из нашего загруженного конфига. Создание главного объекта bot производится от xmpp.Client с передачей домена, на котором находится юзер, и пустым списком, чтобы на экран не выводилась отладочная информация (тру-хакеры работают только методом научного тыка).
Чтобы иметь полный контроль над ботом в любой точке программы (и в главном цикле, и в плагинах), мы будем передавать наш объект bot повсеместно. Но ведь конфиг, список плагинов и другая служебная информация также может понадобиться (например, плагину help нужно знать, какие плагины установлены)? Да, и поэтому в объект bot мы сохраним всю интересную инфу. Так, конфиг сохраним строкой:
Можно законектиться и пройти аутентификацию:
Прием сообщения в xmpppy реализуется через привязку функции к событию прихода сообщения. Сначала нужно создать функцию, к примеру, message, а потом, методом bot.RegisterHandler зарегистрировать ее:
Теперь в цикле необходимо вызвать bot.Process(1), который принимает входящие сообщения и обрабатывает их. Разумеется, вечный цикл здесь не нужен, поэтому в свойстве online смело запишем единичку, и будем крутить цикл до тех пор, пока эта единичка не изменит свое значение:
Для остановки бота нам будет достаточно изменить 1 на 0. В результате, он корректно отсоединится от сервера и завершит свою работу. Когда наш бот запущен, мы можем вернуться к написанию функции обработки входящих сообщений. Простейший вариант, способный принимать сообщение и реагировать на сей знаменательный факт, выглядит примерно так:
В начале этого участка кода мы командой «global bot» получаем доступ к объекту нашего бота. Далее идет обработка входящего сообщения, где с помощью команды mess.getBody мы получаем сообщение. Если входящее сообщение равно None, это означает, что пришла служебная команда (допустим, про то, что юзер что-то нам печатает). Такие сообщения мы обрабатывать не настроены, и реакция на них однозначна – выход из функции. В следующей части Марлезонского балета мы воспользуемся методом send, отправив в ответ простое сообщение. Этот метод в качестве параметра принимает объект Message библиотеки xmpppy. При ее создании мы передаем два параметра: первый - кому нужно отправить сообщения, а второй - текст сообщения. ПлагиныПростая поддержка плагинов реализована в самой библиотекой xmpppy, но мы сделаем ее по-своему, написав собственную архитектуру плагинов (как минимум, с блэкджеком!). Сначала определимся со структурой плагина. Каждый плагин будет иметь название, аналогичное исполняемой им команде. К примеру, если мы хотим создать плагин, который на команду «echo some text» посылает сообщение с этим же текстом, то плагин должен будет называться echo и размещаться в файл echo.py в каталоге plugins. В каждом плагине должны содержаться две функции. Одна, init, может проводить некую предварительную инициализацию и обязательно возвращать 1, если плагин можно использовать лишь админам, и 0, если всем юзерам. Вторая же обязательная функция – run – в качестве входного параметра принимает ссылку на наш бот и входящее сообщение. К примеру, плагин echo выглядит так:
Напишем функцию, загружающую наши плагины. Поскольку мы уже решили, что они должны находиться в папке plugins, то в этом каталоге нам придется поместить пустой файл __init__.py - таково требование Питона. Это шаманское действо позволит нам импортировать файлы как модуль функцией __import__. Итак, нам нужно загрузить все файлы, размещенные в папке plugins (кроме __init__.py) в какую-то переменную. Кроме того, во время инициализации мы создадим два списка, которые будут содержать реестр загруженного. В первом (public_commands) будут перечислены те плагины, которые можно запустить без авторизации, а во втором (commands) – элитные (тру, VIP) - админские плагины. В качестве результата работы функция вернет нам ассоциативный массив с плагинами и списками:
Эта функция очень важна для понимания работы Питона в плане загрузки плагинов «на лету» - советую подробно в ней разобраться. Как видно, после загрузки в переменную plugins плагина, его оттуда можно вытянуть функцией getattr с указанием в первом параметре имени переменной, где хранятся плагины, а во втором параметре – имени плагина. В результате, функция возвращает сам плагин, из которого уже потом можно вызывать функции, в нем находящиеся. К примеру, в вышеуказанной функции загрузки мы запускаем для каждого из них функцию init. В зависимости от результата имя плагина добавляем или в список public_commands, или в commands.
Чтобы вызвать наш плагин, напишем маленькую функцию, которая будет принимать имя команды, ссылку на бота и входящее сообщение:
Эта функция, исходя из имени плагина (параметр command), вытаскивает его из bot.plugins['plugins'] и запускает функцию run. Обработка сообщенийОсталось изменить функцию message, которая обрабатывает входящие сообщения. Логика ее работы такова:
Кстати, рассмотрим один момент. Имя пользователя, которое возвращает команда mess.getFrom(), не всегда возвратит правильное значение (с точки зрения проверки авторизации). Почему? Ведь там может быть имя ресурса (смотри врезку про Джаббер), которое нам не нужно. Стало быть, ресурс мы должны обрезать, например, вызвав метод строки split('/') и потом взять первый элемент списка. Полностью функцию смотри на соответствующей врезке. Исполнение командНу, вот и настало время закодить «виновника торжества» - плагин, исполняющий системные команды. Наречен он будет говорящим именем «cmd», а принцип его работы будет таков:
Вот как это выглядит в строках сурового программного кода:
Happy endНа этом разработку бота можно считать завершенной. Мы реализовали все, что хотели, оставив задел для неограниченного расширения функционала при помощи плагинов. На диске, кроме вышеперечисленных плагинов, содержатся и плагины бонусные. А чтобы продемонстрировать запуск и работу готового бота, я записал видео. Если что-то будет непонятно – спрашивай! Ссылки на объекты и переменныеВ Питоне (да и в других языках, например, в PHP), есть один нюанс, про который часто забывают. Рассмотрим пример работы с переменными:
Он выведет на экран 1, потом 2, и это закономерно. А что, если переменная будет в объекте? Посмотрим:
Сюрприз! На экран выводятся сплошные двойки! А случается это потому, что операция «=» при работе с объектами не копирует их, как при других типах переменных, а создает ссылку. В контексте нашего примера это означает, что переменную bot мы обычно передавали как бы по значению, а на самом деле – как ссылку. Процедура обработки входящих сообщений d
Проект JabberJabber — система мгновенного обмена сообщениями и информацией о присутствии на основе открытого протокола XMPP. Проект был основан Джереми Миллером в начале 1998 года и стартовал с разработки сервера jabberd. В настоящий момент есть некоторая непонятность в отношении терминов jabber и xmpp. Даже в английской Википедии с jabber стоит переадресация на xmpp. Эта непонятка в первую очередь связана с тем, что под именем xmpp протокол был стандартизирован в IETF. Каждый пользователь в джаббер-сети имеет уникальный идентификатор — Jabber ID (сокращенно JID). Адрес JID содержит имя пользователя и доменное имя сервера, на котором зарегистрирован пользователь. Подобно адресу электронной почты, они разделены знаком @. Пользователь может иметь одновременно несколько подключений, для различения которых используется дополнительное значение JID, называемое «ресурсом» и добавляемое через слеш в конец адреса. К примеру, пусть полный адрес пользователя будет Подробнее о xmpppyБиблиотека xmpppy содержит много полезных объектов. Рассмотрим некоторые из них. Объект JID для работы с Jabber ID. При создании принимает параметр - Jabber-идентификатор. Методы:
Главный объект Client. При создании принимает домен джаббер-сервера и переменную для отладочной информации. Методы:
Методы:
DVDНа диске ты найдешь полные исходники бота. VIDEOВидео, иллюстрирующее работу с ботом, ждет тебя там же – на нашем DVD. WWWxmpppy.sourceforge.net – cайт библиотеки xmpppy. |