Методы и подступы к задаче

Настоящий программист должен придерживаться двух основных правил:

а) Сложные задачи превращать в несколько простых
б) Разбивать сложную программу на несколько простых и понятных блоков

Из теории…

Любая задача программиста решается не сразу на компьютере в каком либо редакторе: будь то Delphi или обычный Basic… Структурированное программирование облегчает жизнь не только самим программистам, но и пользователю и его компьютеру, так как обращение к ОДНОЙ процедуре НЕСКОЛЬКО раз занимает гораздо меньше времени и программа весит намного меньше. У тебя два выбора: либо писать программу в лоб: легко запутаться и некоторые элементы будут повторяться несколько раз, что приводит к засорению программы дубликатными строками кода. Либо же программировать структурированно (используя процедуры, модули, функции…). Современные языки высокого уровня поддерживают такие возможности.

Задача

Чем мы сегодня займемся? Создадим искусственный интеллект, то бишь своего бота для IRC (небольшое подобие eggdrop ;)) Если ты IRC-мэн и зависаешь там часами — ты меня поймешь. Если ты не знаешь, что такое IRC, то скорее трави своего ослика IE на
http://www.irc.ru — там тебе помогут разобраться с данной проблемой 🙂

Принцип работы бота

Если ты немного знаешь протокол TCP, то ты в курсе, что IRC — это вид сервиса, сидит как правило на 6667 порту и ждет в гости меломанов 😉 После успешного коннекта, клиент (mIRC, Pirch, BitchX, etc…) передает твой nick, ident, username и пароль на подключение (если он нужен) и демон впускает тебя на IRC-просторы, периодически посылая PING и ожидая в ответ PONG, тем самым проверяя тебя на живучесть… Эта особенность очень важна, т.к. если твой бот не отпингуется по истечении 90 секунд — сервер его просто отключит с резоном Ping
timeout.

Для написания бота используем язык Perl — жемчужина, содержащая регулярные выражения и стоящая почти на любой Linux-машины… Официальный сайт языка
http://www.perl.com, эмулятор под форточки можно слить с
http://www.activeperl.com.

Святая святых — конфиг файл для бота =)

В файле bot.conf определим важные переменные, которые будут использоваться в самом коде. Я реализовал самый простой вариант — перловый файл, который читается в начале работы основного файла. В нем определяем такие переменные как:

$module_path — директория модулей, относительно главного файла, либо относительно корня.
@modules — массив содержащий имена твоих «irc-модулей», которые будут использоваться ботом, например ( @modules=(‘server’,’irc’); )

Переменные модуля server:
$host, $port — хост и порт IRC-сервера.
$botnick,$username,$infoname,$serverpass — ник, идент и информация о боте, пароль для коннекта на сервер (если нужен).

Переменные модуля IRC:
@channels — массив содержащий каналы, на которые будет джойниться бот (первый из них — канал по умолчанию).

Пишем главный файл — bot.pl

Задача главного файла — обьявить все модули и файлы, которые будут загружены в память и запустить процедуру порождения сокета. Он выглядит примерно так:

#!/usr/bin/perl
use Socket;
use IO::Handle;
$config=$ARGV[0] || ‘bot.conf’;
do $config;
foreach (@modules) {
chomp;
require $module_path.$_.’.pl’;
}
&init;

Конфиг файл задается либо через параметр бота, либо берется по умолчанию —
bot.conf. init — процедура порождения сокета в модуле server.

Модули бота

В этой статье я распишу 2 модуля для твоего бота: server и irc. Server — работает конкретно с локальной машиной и созданным сокетом, Irc — с irc-командами.

## server.pl ##

#!/usr/bin/perl
print «Plug in Server module…\n»;

sub init {
while (1) {
while (!socket(SOCK, PF_INET, 1, getprotobyname(‘tcp’))) { print «socket: $!\n» }
autoflush SOCK 1;

&connect;

close(«SOCK») || &logging(«close: $!\n»);
}
}

### Процедура создания сокета SOCK и порождение бесконечного цикла до закрытия сокета. После открытия сокета вызывается процедура connect — соединение с IRC-сервером.

sub connect {
my ($iaddr, $paddr, $line);
$iaddr = inet_aton($host) || die «invalid host: $host»;
$paddr = sockaddr_in($port, $iaddr);
&logging(«Connecting to $host at $port… «);
while (!connect(SOCK, $paddr)) { print «connect: $!\n» }
&logging(«connected.\nSending info… «);
&send(«NICK $botnick\nUSER $botnick $botnick $botnick :$botnick\n»);
&send(«PASS $servpass») if $servpass;
&join;
while ($line = shift @CMDBUF || <SOCK>) { 
&irc_cmd(«$line»);
}
}

### Процедура коннекта на сервер — передача сведений о боте (процедура send — передача комманд через сокет) с последующим заходом на каналы IRC. Здесь же организуется и получение информации с сервера — полученная строка хранится в переменной $line, которая посылается на обработку в модуль IRC в процедуру &irc_cmd.

sub send {
my ($string, $to)=@_;
$to=»SOCK» unless $to;
print $to «$string\n»
}

### Процедура send — отправка команды на socket — тут все ясно, с первого взгляда на код… Если сокет не определен в параметрах процедуры — используется стандартный — SOCK.

sub join {
my ($chan,$opt)=@_;
foreach (@channels) {
chomp;
&send(«JOIN $_»)
}
$defaultchan=$channels[0];
return
}

### Процедура захода на IRC-каналы (заход на каждый канал массива @channels) и определение главного канала.

## irc.pl ##

#!/usr/bin/perl
print «Plug in irc module\n»;

sub logging {
my ($string, $to)=@_;
if (!$to) {
print «Console: $string\n»;
return
}
open(«LOG»,»>>$to») || &logging(«$!\n»);
print LOG «$string\n»;
close(«LOG»)
}

### Процедура записи что-либо в лог файл. Если файл не задан — идет запись на консоль.

sub irc_cmd {
my ($prefix, $command, $params, $servername, $nick, $user, $host, $trailing, $middle, $params2, $trailing2) = get_vars($_[0]);

&send(«PONG :$params») if ($command eq ‘PING’);
if ($command eq ‘PRIVMSG’) { 
if ($middle eq $botnick) { &priv_msg($prefix, $servername, $nick, $user, $host, $trailing, $middle, $params2, $trailing2)
} else {
&pub_msg($prefix, $servername, $nick, $user, $host, $trailing, $middle, $params2, $trailing2) }
}
}

### Самая главная процедура — обработка команд — вначале — обработка переменных в процедуре &get_vars, затем выборка команд. Если пришел запрос на PING — отпинговываемся, посылая PONG. Если кто-то что-то сказал — разделение на 2 процедуры — приватный базар, либо фраза на канале.

sub get_vars {
my ($prefix, $command, $params);
my ($servername, $nick, $user, $host);
my ($trailing, $middle, $params2, $trailing2);
$_[0] =~ /(:(\S+) )?([a-zA-Z0-9]+) (.+)\x0D+/;
($prefix, $command, $params) = ($2, $3, $4);
$prefix =~ /((\S+)!(\S+)@([a-zA-Z0-9.]+))|([a-zA-Z0-9.]+)/;
($servername, $nick, $user, $host) = ($5, $2, $3, $4);
$params =~ /:(.*)|(\S+) (.+)/;
($trailing, $middle, $params2) = ($1, $2, $3);
$params2 =~ /:(.*)/;
$trailing2 = $1;
return ($prefix, $command, $params, $servername, $nick, $user, $host, $trailing, $middle, $params2, $trailing2)
}

### Вот тут- вся прелесть Perl — регулярные выражения… Всего в несколько строк кода — определяются 12 переменных, которые очень важны в работе программы. Не буду рассказывать про них — если захочешь их узнать — сам прочитаешь об этом.

sub msg {
my ($msg, $to)=@_;
$to=$defaultchan unless $to;
&send(«PRIVMSG $to :$msg»)
}

### Упрощенный вариант &send — фраза в канал или в приват…

sub pub_msg {
&msg(«Кто-то, что то сказал…»);
}

### А здесь ты уж сам пофантазируй, что хочешь сделать… Пиши скрипты, либо оформляй в отдельный файл. А в самой процедуре лишь обработай его: do «путь/к/скрипту»;

Как запускать?

После отладки bot.conf выполни команды на удаленной машине:

chmod +x bot.pl
./bot.pl [conf-file] &

Вот собственно…

На этом я остановлюсь. Многое в этом боте недоработанное — я умышленно не стал дорабатывать мелочи ($altnick — если ник занят, получение времени и т.д.). Если ты изучишь Perl — ты поймешь как реализовать это…

Цель статьи — научить тебя писать не бота, а программировать разумно, используя процедуры и внешние файлы… Код компактен и производительность выше.

Оставить мнение

Check Also

Пространство для эксплуатации. Как работает новая RCE-уязвимость в Apache Struts 2

Во фреймворке Apache Struts 2, виновном в утечке данных у Equifax, нашли очередную дыру. О…