Все материалы предоставлены только с ознакомительной целью
Главная — Статьи — Кодинг — Perl. IRC бот
Perl. IRC бот
© Cobalt 18.05.2006 статья не оптимизирована
© Cobalt 18.05.2006 статья не оптимизирована
Скачать исходники В этой статье мы попытаемся охотиться сразу на двух зайцев. А именно, разберем IRC протокол и напишем скрипт на Perl, который будет выполнять функции IRC бота. Эта статья посвящена в первую очередь програмистам которые хотят изучить Perl, и подразумевается что они хотябы знают синтаксис данного языка. Для начала опишем настроечные переменные, а именно: 1. Адрес и порт сервера. 2. Ник и идент 3. IRC канал на котором будет крутиться наш бот. Это делается так: $host="irc.lxxl.info"; $port="6667"; $nick="MyBot"; $ident="MyBot"; $chan="#GFS"; Советую использовать в качестве сервера именно irc.lxxl.info, он не очень строг к вашему иденту ). Далее создадим сокет соединения с сервером: use Socket; socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname(\'tcp\')) or die "Couldn`t create socket : $!\n"; $iaddr=inet_aton($host); $paddr=sockaddr_in($port,$iaddr); print "\n>> Connecting to $host:$port...\n"; connect(SOCK, $paddr) or die "Couldn`t connect to $host:$port : $!\n"; for($i=0; $i<4; $i++){ $data=; print($data); } Рассмотрим этот кусок кода более подробно. use Socket - сдесь мы указали что необходимо использовать стандартную библиотеку Socket, которая содержит функции и переменные для работы с сокетами. Функция socket предназначена для создания сокета. В ее первом параметре указывается идентификатор по которому мы в последствии будем обращаться к сокету. Во втором указывается комуникацион- ный домен, в нашем случае это PF_INET, что означает домен Интернет. Так же существует домен unix - PF_UNIX. Третий параметр указывает тип сокета. Мы будем использовать SOCK_STREAM - этот тип обеспечивает последовательный, надежный поток байтов. Так же существуют Datagram socket и Raw socket, но о них как- нибудь в другой раз. И наконец в последнем параметре определяется протокол. Делается это функцией getprotobyname(\'tcp\'), которая указывает в качестве протокола TCP. Так же возможно использовать и udp, ip и т.д. Функция getprotobyname возвращает название протокола в более удобном для функции socket виде. Далее необходимо преобразовать адрес сервера в бинарную последовательность. Для этого используется функция inet_aton(). И дописать в нее порт к которому мы будем соединяться функцией sockaddr_in(). Теперь соединим сокет с сервером функцией connect(). Если все прошло успешно, в указателе SOCK у нас будет поток сокета. Работать с ним можно точно также как и с потоком файла. Например при успешном соединении сервер должен послать нам четыре строки служебной инфы. Вот мы их прочитаем и выведем на экран в цикле For. Теперь необходимо послать серверу информацию о себе. А именно идент и ник. Для этого нам понадобится вункция send() которая принимает в качестве аргумента три параметра: идентификатор сокета, строку которую необходимо отослать и 0. Вместо нуля могут быть установлены следующие флаги: MSG_OOB - Посылать/получать данные, характерные для сокетов типа SOCK_STREAM MSG_DONTROUTE - Посылать данные без маршрутизации пакетов. Как правило используется диагностическими программами и процессами управляющими таблицами маршрутизации. Посылаем данные, читаем 10 строк и выводим на экран: print ">> Sending NICK and IDENT...\n"; send (SOCK, "NICK $nick\n", 0); send (SOCK, "USER $ident localhost localhost :$nick\n", 0); for($i=0; $i<10; $i++){ $data=; print($data); } В соответствии со стандартом IRC протокола, мы обязаны при соединении уведомить сервер о том кто мы такие. Это делают IRC команды NICK и USER. Их синтаксис не сложно понять из запросов которые мы только что отослали серверу. Команда JOIN предназначается для входа на какой-либо канал. Отсылаем print ">> Join chanel $chan...\n"; send (SOCK, "JOIN $chan\n", 0); для входа на указаный канал. Заметь, что каждая IRC команда оканчивается обязательным символом \n (перенос строки). Ведь ты нажимешь Enter в mIRC ?=) Давай запустим бесконечный цикл приема сообщений: while($data=){ print($data); } close(SOCK); Теперь твой бот висит на канале и слушает что ему говорят. В окошке консоли отлично видно какая информация приходит с сервера. Но возникает одна существен- ная проблема: нашего бота через некоторое время выкидывает с сервера по таймауту Это происходит из-за того что бот не отвичает на пинги посылаемые сервером. Да да, на сервере установлен миханизм защиты от зомби. Каждому своему клиенту через определенное время посылается команда PING, на которую в течении 256-ти секунд клиент обязан ответить командой PONG с копией переданых данных. Если же сервер по прошествии 256-ти секунд не получает ответа, он закрывает свое соединение с клиентом, считая его зависшим. Выходом из данной ситуации будет служить если мы пропишем в цикле бота обработ- чик пингов.Т.е. нам необходимо уловить когда придет комада содержащая слово PING взять данные пришедшие вместе с ней, и отослать в месте с командой PONG обратно серверу. @part=split(/:/,$data); if(@part[0] eq "PING "){ print ">> PONG :@part[1]\n"; send (SOCK, "PONG :@part[1]\n", 0); } Функция split разбирает строку в переменную, считая в качестве разделителя первый аргумент. В нашем случае это простейшее регулярное выражение /:/, которое указывает в качестве разделителя двоеточие. После у нас в массиве @part содержатся элементы строки $data. eq - аналогично знаку равенства для строк. Теперь было бы не плохо если бы наш бот хоть что-то бы делал, а не просто сидел и молчал на канале. Давай напишем обработчик излюбленной всеми команды !пиво ). Получая такую команду наш бот должен проверить, идет ли за ней чей-то ник. Если да, то он должен сказать что пославший команду налил указаному нику пива. Если же не идет, то бот обязан сказать что пославший команду налил всем пива. ### парсим полученую строку. chop($data); ## обрезаем символы конца у строки chop($data); ## обрезаем символы конца у строки @part=split(/:/,$data); @tmp=split(/!/,@part[1]); $_user=@tmp[0]; # $_user - задавший команду @tmp=split(/ /,@tmp[1]); $_id=@tmp[0]; # $_id - идент юзера @tmp=split(/ /,@part[1]); $_cmd=@tmp[1]; # $_cmd - IRC команда @tmp=split(/ :/,$data); $_data=@tmp[1]; # $_data - команда Эти переменные пригодятся тебе а дальнейшем. Теперь осталось только проверить $_data на наличие !пиво в начале строки. делается это через регулярные выражения: if($_data=~/^!пиво/){ ## $_data содержит !пиво в начале строки } Надо разобрать $_data используя в качестве разделителя пробел, и проверить наличие 2-го элемента массива. Если он присутствует, то выдаем первый вариант ответа, если же нет - второй. @part=split(/ /,$_data); if(@part[1]){ send (SOCK, "PRIVMSG $chan :$_user налил пива @part[1]\n", 0); }else{ send (SOCK, "PRIVMSG $chan :$_user налил всем по пиву\n", 0); } IRC команда PRIVMSG предназначена для отправки сообщений. Если вместо $chan указать ник юзера, то сообщение будет отправлено ему в приват. Вне всяких сомнений, тебе ни составит ни какого труда написать обработчики для своих команд. Надеюсь алгоритм ты понял. Что же касается неописаных сдесь команд IRC, то тебе достаточно просто посидеть и посмотреть какие данные выдает бот в консоль при появлении той или иной команды на сервере или канале. Так же советую скачать бота посложнее с нашего сайта. Он тоже написан на перл, но в нем реализовано довольно много интересных функций. Например поддержка добавления кода прям на ходу не перегружая бота. Скачать его можно по этой ссылке. Ну вот и все на cегодня. До скорых встреч! =)
© Cobalt 18.05.2006 статья не оптимизирована

"Эта статья посвящена в первую очередь програмистам которые хотят изучить Perl" - тогда лучше использовать use IO::Socket.
Для пинг-понга легче использовать следюующий код:
if (m/^PING (.*?)$/) { print $socket "PONG $1n"; }
"Выдрать" ник, идент, msg и канал можно легче:
if (m/^(.*?)!(.*?)(s)PRIVMSG(s)(.*?):(.*)/) {
} где $1 = nick, $2 = ident, $5 = channel, $6 = msg.
Посмотрел вашего irc-бота. "операторские команды" можно было сделать намного проще:
if (m/!mode(s)(.*?)(s)(.*)/) { print $socket "MODE $2 $4n"; }
Да и нужно сделать нормальную авторизацию, например командой !login <password>.
P.S. Писал на ночь, если где-то ошибся исправьте. Думаю мои идеи итак ясны.