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

LXF100-101:Программирование на Perl

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

Программирование: личный IRC-бот

ЧАСТЬ 1: Любите покодировать, но нет свежих идей? Майк Сондерс затевает серию публикаций о полезных мини-программах. Начнем с персонального IRC-бота…

Добро пожаловать в новую серию публикаций о программировании. В трех статьях этого цикла мы покажем, как писать полезные программы, а затем расширять и улучшать их по мере роста вашего умения. Ключевое слово здесь – «программа»: мы не будем копаться ни в теории, ни в сугубо техническом мусоре; вместо этого рассмотрим классные штуки, которые можно вытворять в различных языках программирования. Ведь гораздо интереснее писать настоящую программу, чем тратить время на изучение нудных конструкций циклов! А если вы уже пробовали кодировать, проблем с пониманием у вас не возникнет.


Начнем с IRC-бота, написанного на Perl. Если вы интернетчик со стажем, то наверняка пользовались IRC; если нет – прочтите врезку «Стоп… а что такое IRC?», там объясняются базовые понятия. Коротко говоря, IRC – это чат в реальном времени, часто используемый разработчиками открытого ПО для взаимодействия друг с другом. Он очень быстр, в нем легко разобраться, и – главное – можно создавать виртуальных участников чата.

Этим мы и займемся – напишем «робота», который присоединяется к чату и рассказывает о компьютере. Скажем, вы на работе или в отпуске далеко от дома и хотите наблюдать за домашней системой (или за сервером на площадке провайдера). Наш IRC-бот будет сидеть себе в чате и ждать, когда появитесь вы и спросите его о времени работы системы и ее загрузке.


Программ для мониторинга системы немеряно, но они страдают повышенной дотошностью и забивают ваш почтовый ящик письмами с лишними подробностями о работе компьютера. С нашим ботом все намного проще: если вы хотите узнать, сколько осталось свободного места на диске, или посмотреть, хватает ли оперативной памяти, просто заскочите в IRC и спросите у него (через приватные сообщения). А самое важное – мы ограничим доступ так, чтобы эти данные могли получить только вы!

Букварь на Perl

Для начала пройдемся по основам Perl. Вы, небось, подумали: «А с какой стати писать этого бота на Perl?» Хороший вопрос. Ведь Perl – это язык скриптов, изначально задуманный для обработки текста. Однако он также снабжен массой модулей расширения, один из которых существенно упрощает взаимодействие с серверами IRC.

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

print “Привет!”;

Все выражения завершаются точкой с запятой (;), в стиле языка C. Имена переменных начинаются со знака доллара:

$num = 25;
$string = “гамбургеров”;
print “Я съел $num $string\n”;

Как видите, Perl не задумывается о типах переменных – он определяет их на лету. Аналогично C, символ \n означат перевод строки. Массивы объявляются с символом @:

@distros = (“Ubuntu”, “Fedora”, “SUSE”);
print $distros[0];

Этот код выводит на экран слово “Ubuntu”. По принципу многих других языков программирования, первый элемент массива получает номер 0. Для массивов полезна команда shift – она удаляет первый элемент массива и помещает его в переменную:

@distros = (“Ubuntu”, “Fedora”, “SUSE”);
$foo = shift(@distros);
print “$foo\n”;
$foo = shift(@distros);
print “$foo\n”;

Мы создали массив из трех строк, затем сдвинули (shift) его первый элемент в переменную $foo, вывели ее и сдвинули массив еще раз. Итак, сначала выводится строка “Ubuntu”, за ней – “Fedora”, и в итоге в массиве остался единственный элемент, “SUSE”. Расширенная версия массива – это «словарь» (или «хэш»), объявление которого начинается со знака процента (%):

%weight = (“Mike” => 8, “Paul” => 12, “Nick” => 25);

Здесь список слов связывается со значениями. Например, вы хотите вывести вес (weight) записи Nick:

print $weight{“Nick”};

Выведется 25. if устроен точно так же, как в большинстве других языков:

$x = 64;
if ($x == 64) {
print “Yep, x is 64!\n”;
}

И, наконец, подпрограммы (функции) объявляются просто командой sub:

sub saystuff {
print “This is saystuff!\n”;
}
saystuff();

Таковы «колесики и винтики» программирования на Perl – все, что нам нужно, чтобы начать писать IRC-бота!

Установка

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

Вы можете найти Net::IRC с помощью своего менеджера пакетов (он называется perl-irc или net.pm или как-то вроде этого). А не найдете, не беда: собрать его из исходных текстов проще простого [даже термин «собрать» здесь избыточен: Net::IRC написан на Perl, так что речь идет просто о копировании его в нужный каталог, – прим.ред.]. Зайдите на сайт http://search.cpan.org/dist/Net-IRC/ или откройте раздел Development/Net-IRC нашего DVD (первая сторона) и скопируйте файл Net-IRC-0.75.tar.gz.

Распакуйте архив, зайдите в образовавшийся каталог и введите команды:

perl Makefile.PL
make
make install

Последнюю из них нужно выполнять с правами администратора. Она установит модуль Net::IRC, и программа, которую мы напишем, сможет его применить!

Далее создадим две учетных записи для IRC: первая – для вас, вторая – для бота (если вы завсегдатай IRC, то первая у вас уже есть). Это важный этап, поскольку из-за борьбы со спамом на многих IRC-серверах пользователь должен зарегистрироваться, чтобы иметь возможность отправлять приватные сообщения другим участникам чата.

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

/msg nickserv register <password>

(<password> замените на подходящий пароль). Примеры команд приведены для серверов Freenode, в других сетях они могут отличаться. Теперь ваш пользователь зарегистрирован на сервере IRC – то есть никто другой это имя не отберет. Сказать IRC-серверу, кто вы такой, можно с помощью команды:

/msg nickserv identify <password>

Нужно также зарегистрировать как пользователя нашего, пока нерожденного, бота. Переключитесь в другого пользователя и задайте ему пароль таким образом:

/nick UltraCoolLXFBot
/msg nickserv register thisismypassword

Конечно, можно взять другое имя пользователя и пароль, но запомните их, ведь они понадобятся коду нашего бота! Итак, мы завели две зарегистрированные учетные записи, себе и боту.

Покажи мне код

Мы готовы к запуску бота. Вот его код. Его не придется набирать вручную: он приведен на нашем DVD в разделе Magazine/Perl (файл bot1.pl). Но прежде чем запускать бота, не мешает просмотреть код и его описание...

use Net::IRC;
$server = ‘irc.freenode.net’;
$channel = ‘#blergh’;
$botnick = ‘MegaMikeBot’;
$password = ‘foobar’;
$botadmin = ‘M-Saunders’;
$irc = new Net::IRC;
$conn = $irc->newconn(Nick => $botnick, Server => $server, Port => 6667);
$conn->add_global_handler(‘376’, \&on_connect);
$conn->add_global_handler(‘disconnect’, \&on_disconnect);
$conn->add_global_handler(‘kick’, \&on_kick);
$conn->add_global_handler(‘msg’, \&on_msg);
$irc->start;
sub on_connect {
$self = shift;
$self->privmsg(‘nickserv’, “identify $password);
$self->join($channel);
$self->privmsg($channel, “Lo! I’m just a silent bot.”);
}
sub on_disconnect {
$self = shift;
$self->connect();
}
sub on_kick {
$self = shift;
$self->join($channel);
$self->privmsg($channel, “Please don’t kick me!);
}
sub on_msg {
$self = shift;
$self->privmsg($botadmin, `uptime`);
}

Давайте пройдемся по коду. Первая строка просто сообщает интерпретатору Perl, что мы хотим использовать ранее установленную библиотеку Net::IRC. На пяти следующих строках объявляются параметры конфигурации – перед запуском скрипта их потребуется изменить. Мы определяем адрес IRC-сервера, с которым соединится наш бот, и канал, в который он войдет; затем задаем имя пользователя (ник) и пароль, с помощью которых бот авторизуется на IRC-сервере. Наконец, задается имя пользователя, с которым бот будет говорить – ваша обычная учетная запись IRC.

После этого в строке $irc = new Net::IRC; создается объект IRC-бота, с творчески выбранным именем $irc. Затем мы формируем объект $conn, который создает соединение с IRC-сервером на основе предоставленной ранее информации. Итак, на данном этапе у бота есть все необходимое для подключения к IRC-серверу.

Но сперва нужно сделать еще одну вещь: связать с сообщениями, которые бот получает от IRC-сервера, некоторые действия. Наш бот ведь не глухонемой, и он должен знать, на какие сообщения давать ответ. Четыре вызова add_global_handler об этом позаботятся – они говорят боту: «Получив сообщение А, перейди к подпрограмме B».

Например, первая строка этого куска кода говорит: «Если получим сообщение 376, вызываем функцию on_connect». Сообщение 376 – специальный код, возвращаемый IRC-сервером на стадии установки соединения: сервер доставил информацию, что связь успешно установлена, и готов к приему команд. Если вы любитель посидеть в IRC, то знаете, что во время установки соединения на экране появляется множество сообщений о статусе сервера, сообщений данного сообщества и т.д. Код 376 означает конец приветствий.

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

Команда $irc->start; приводит бота в действие. Бот обучен, какой сервер и канал использовать и какие подпрограммы вызывать при получении определенных сообщений; он соединяется с сервером и через несколько секунд получает сообщение 376, о котором мы говорили ранее. Оно активирует функцию on_connect.

В этой функции вы увидите команду shift, которая была описана в разделе с примером на Perl. Можете не вникать в суть кода, вкратце скажем, что он делает: аргументы передаются функции в массиве, и первый из них – вызывающий объект. Значение первого аргумента, вызывающий объект, присваивается переменной $self, тогда можно будет вызывать его функции. Есть и другие варианты, но наш обеспечивает модульность кода.

После соединения бота с сервером мы отправляем приватное сообщение (privmsg) nickserv – это обработчик имени пользователя на большинстве серверов IRC. Мы предоставляем пароль бота для аутентификации его на сервере. Затем мы заходим в заданный канал и отправляем небольшое сообщение, означающее, что мы будем вести себя тихо, не засоряя канал всяким хламом!

Теперь бот будет смирно сидеть в канале, пока его оттуда не пнут или не прервется соединение. В этих случаях он вызовет соответствующие функции и кротко попросит: «Пожалуйста, не удаляйте меня» (“Please don’t kick me!”) или повторно создаст соединение. Самая важная функция здесь – это on_msg. Она вызывается, когда бот получает приватное сообщение – то есть сообщение в отдельном диалоге вне основного канала. В ответ на любое сообщение бот отправляет вывод команды `uptime` пользователю $botadmin, т.е. вашему обычному пользователю.

Обратите внимание на обратные одиночные кавычки (`) вокруг команды `uptime`. Они велят Perl выполнить указанную команду [в оболочке, – прим.ред.] и вернуть результат в виде строки. Таким образом, строка с privmsg отправляет нашему пользователю приватное сообщение, содержащее вывод команды uptime. На большинстве клавиатур клавиша одиночной обратной кавычки находится под клавишей Escape.

Продолжаем работать

Пора запустить бота. Откройте файл bot1.pl и измените пять конфигурационных переменных сверху в соответствии с вашими настройками – а именно, впишите имя пользователя и пароль для бота, вами зарегистрированные, а свое имя пользователя присвойте переменной $botadmin. Запустите IRC-клиента, авторизуйтесь на сервере и зайдите в канал, который вы указали для бота. Затем запустите бота:

perl bot1.pl

Через пару секунд бот установит соединение, авторизуется на сервере и зайдет в указанный канал. Вы узнаете о его появлении по небольшому приветствию! Командой privmsg начните приватный диалог с ботом и скажите ему все, что угодно – бот пришлет результат выполнения команды uptime. Отлично! Теперь можно запустить бота на любом компьютере и отслеживать время работы системы (и уровни загрузки), просто общаясь с ботом в IRC-чате.

Хотя нарекания остаются. Кто бы ни заговорил с ботом в приватном канале, результат выполнения команды uptime отправится вам. Бот также умеет выполнять только команду uptime – может, вам этого и достаточно, но он способен на гораздо большее.

Если все работает нормально, можно добавить в последнюю функцию (on_msg) новые возможности, например, таким образом (полный код бота – в файле bot2.pl на DVD):

sub on_msg {
$self = shift;
$event = shift;
if ($event->nick eq $botadmin) {
 foreach $arg ($event->args) {
  if ($arg =~ m/uptime/) {
   @output = 'uptime';
   foreach $line (@output) {
    $self->privmsg($botadmin, $line);
   }
    }
   if ($arg =~ m/df/) {
    @output = 'df';
    foreach $line (@output) {
    $self->privmsg($botadmin, $line);
    }
   }
  }
 }
}

На этот раз мы проверяем (if), кто отправляет боту приватное сообщение (третья строка), и продолжим только если это $botadmin, то есть вы! Посмотрим на текст, отправленный боту. Он содержится в массиве $event->args.

Пройдемся по тексту и выясним, есть ли там команда, подлежащая выполнению. Этим занимается строка if ($arg =~ m/uptime/): она означает «Если одна из строк текста, отправленного боту, содержит строку uptime, сделай следующее...» Фрагмент m/uptime/ – регулярное выражение, проверяющее, есть ли в тексте строка uptime.

Если она есть, запустим команду uptime и запишем результат ее выполнения в массив @output. Потом в цикле выведем каждую строку текста, содержащегося в массиве.

Uptime возвращает только одну строку текста – может, не стоит связываться с циклом? Но в следующем фрагменте кода проверяется, отправили ли мы команду df – а df возвращает несколько строк. Получив команду df, бот копит ее результат и отсылает его нам, строка за строкой.

Добавляйте новые команды, сколько душа просит. Для этого копируйте и вставляйте блок кода с if, а в нем меняйте текст, который там проверяется, и выполняемую команду. Например, можно добавить блок if ($arg =~ m/ps/), который получает список процессов с помощью команды @output = `ps`;. Можно вставить любую команду – бот все слопает!

Теперь у нас есть расширяемый IRC-бот, который отвечает на ваши команды и только вам. Он ни о чем не проболтается случайному посетителю (если только тот не зайдет под вашим именем и паролем). Вряд ли вы захотите разрешить боту выключать компьютер, но с его помощью можно получить о компьютере массу информации.


Это очень гибкое решение: если кажется, что с компьютером что-то случилось, можно запустить бота на web-сервере и допросить его; или – запустить бота на домашнем компьютере и прямо с работы запускать и останавливать с его помощью определенные процессы. Возможности безграничны – а всего-то нужен доступ к IRC!

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