IRC bot (демон на php) — HackZona.Ru

IRC bot (демон на php)

IRC bot (демон на php)

Тип статьи:
Со старой ХакЗоны.
Источник:
Предистория

Установил я значит как-то IRC сервер и радовались пользователи… Но захотели пользователи иметь возможность еще и логи читать (слово ж не воробей!).

Первым, что пришло в голову — запустить на сервере mIRC на постоянную и с него вести логи нужных каналов. Сказано — сделано. Однако не совсем удобно — постоянно держать законектеным mIRC. Вот тогда и зародилась идея о боте...

До этого я с IRC (и с ботами тем более) особо не сталкивался и потому пошел сразу в гугел. После 5 страниц полного бреда (как по моему) я нашел одну статейку (жаль урл не помню!) в которой была на мой взгляд самая что ни на есть ключевая фраза: «чтобы понять как общаются сервер и клиент можно включить в обычном mIRC опцию /debug on и все данные будут сохранятся в лог-файле». Дальше я не читал… Включив опцию, активно пообщавшись — за 2 часа у меня было достаточно информации.

Собственно скрипт бота (IRC Bot script):


#!/usr/local/bin/php
<?php
// Параметры IRC сервера
$irc_server[«ip»]='XXX.XXX.XXX.XXX';
$irc_server[«port»]=6669;
$irc_server[«name»]='irc.mine.net';

// Список каналов на которых будет присутствовать бот
$channels[0]=«somechannel0»;
$channels[1]=«somechannel1»;
$channels[2]=«somechannel2»;

// Таймер для отсчета времни чтобы выдать случайную фразу
$timer=0;

$privates=Array();

// Параметры бота
$bot[«nick»]='PHP_Bot';
$bot[«user»]='PHP_Bot';
$bot[«host»]='bot.mine.net';
$bot[«ip»]='XXX.XXX.XXX.XXX';
$bot[«hz»]='PHP_Bot';

// Функция из мана используемая для получения «наиболее случайных чисел»
function make_seed() {
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}

// Функция установки коннекта к IRC серверу
// Устанавливает начальный коннект
function connect_IRC () {
global $irc_server, $bot, $fp;
// Открываем сокет с заданым сервером и портом
$fp = fsockopen($irc_server[«ip»], $irc_server[«port»], $errno, $errstr,30);
$flag=0;
// Вычитываем все что даст нам сервер, до ключевой фразы, после которой,
// можно отсылать данные о пользователе
while (!$flag && !feof($fp)) {
$line=fgets ($fp);
if (preg_match("/bFoundsyourshostnameb/", $line)) {
$flag=1;
}
}
// Пишем в сокет данные о авторизации бота
fputs ($fp, sprintf («NICK %sn», $bot[«nick»]));
fputs ($fp, sprintf («USER %s „%s“ „%s“ :%sn»,
$bot[«user»], $bot[«host»], $bot[«ip»], $bot[«hz»]));
$flag=0;
// Снова вычитываем все до ключевого номера 700, после которого можно
// начинать заходить в каналы
while (!$flag && !feof($fp)) {
$line=fgets ($fp);
if (preg_match("/b700b/", $line)) {
$flag=1;
}
}
// Возвращаем указатель на открытый сокет основной части
return ($fp);
}

// Функция для подготовки вывода в канал
// одного из предопределенных случайных выражений
function is_time($chan) {
global $timer, $log_file, $bot;
// Полный путь к текстовому файл в котором определены возможные выражения
$texts=file('/home/myaccount/bot_conf/lyaps');
$message='';
$now=mktime(date («H»), date(«i»), date(«s»), date («m»),
date («d»), date («Y»));
// Определяем интервал с которым бот будет что-то говорить в канал
// (в этом примере — 10 минут)
$time_to_say=mktime(date («H»), date(«i»)-10, date(«s»),
date («m»), date («d»), date («Y»));
// Если пора что-то говорить — формируем фразу,
// если нет — возвращаем пустое значение
if ($timer<$time_to_say) {
// Сбрасываем таймер
$timer=$now;
// Определяем фразу
srand(make_seed());
$mess=rand(1, sizeof($texts));
$texts[$mess]=trim($texts[$mess]);
// Форматируем строку для отсылки серверу
$message = sprintf («PRIVMSG #%s :%sn»,$chan,$texts[$mess]);
// Если выражение не пустое пишем в лог
if ($texts[$mess]!='') {
$log_file='/home/myaccount/irc_log/#'.$chan.date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("[%s] <%s> %sn",
date(«H:i»), $bot[«nick»], $texts[$mess]));
fclose ($lf);
}
}
//Возвращаем подготовленую строку
return ($message);
}

// Функция отсылки привествия для зашедших на канал пользователей
// берет одно из предопределенных приветствий и подставляет ник
function hello ($str) {
global $bot;
// Полный путь к текстовому файл в котором определены возможные выражения
$texts=file('/home/myaccount/bot_conf/hello');
// Определяем фразу
srand(make_seed());
$mess=rand(1, sizeof($texts));
// Форматируем строку для отсылки серверу
$message = sprintf («PRIVMSG #%s :%s%s!n»,$str[2],$texts[$mess], $str[1]);
// Пишем в лог
$log_file='/home/myaccount/irc_log/#'.$str[2].date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("[%s] <%s> %s%s!n",
date(«H:i»), $bot[«nick»], $texts[$mess], $str[1]));
fclose ($lf);
// Возвращаем подготовленую строку
return ($message);
}

// Устанавливаем соединение с сервером
if ($sp=connect_IRC()) {
reset ($channels); // Думаю что не нужен, но… на всяк пожарный
while (list(,$channel_name)=each($channels)) {
// Заходим во все определенные каналы
fputs ($sp, sprintf(«JOIN #%sn», $channel_name));
fputs ($sp, sprintf(«MODE #%sn», $channel_name));
// Ниже бот берет на себя права оператора сервера, для этого
// для него должны быть определены соответствующие настройки сервера
// (см. документацию к серверной части)
fputs ($sp, sprintf(«OPER %s PHP_BOT_oper_passwordn», $bot[«nick»]));
fputs ($sp, sprintf(«MODE %s +an», $bot[«nick»]));
fputs ($sp, sprintf(«MODE %s +An», $bot[«nick»]));
fputs ($sp, sprintf(«SAMODE #%s +o %sn», $channel_name, $bot[«nick»]));
// Определение регэкспов для отслеживания ключевых выражений
// Вход пользователя на канал
$joined[$channel_name]=
'/:(.+)!.+sJOINs:#('.$channel_name.')/';
// Сообщение от пользователя в канал
$privmsg[$channel_name]=
'/:(.+)!.+sPRIVMSGs#('.$channel_name.')s:(.+)/';
// Выход пользователя с канала
$partmsg[$channel_name]='/:(.+)!.+sPARTs#('.$channel_name.')/';
// Установка темы канала
$topic[$channel_name]=
'/:(.+)!.+sTOPICs#('.$channel_name.')s:(.+)s/';
// Отключение пользователя от сервера
$quitmsg='/:(.+)!.+sQUITs:Quit:s(.*)s/';
// Сообщение в приват боту
$private_me='/:(.+)!.+sPRIVMSGs('.$bot[«nick»].')s:(.+)/';
// Смена ника юзером
$nick='/:(.+)!.+sNICKs:(.+)s/';
}
}

$flag=0;
// В принципе — бесконечный цикл (если не оборветься связь с сервером)
while (!$flag && !feof($fp)) {
$line=fgets($sp);
reset($joined);
// Проверяем — если идет команда PING от сервера — отвечаем PONG
$ping='/^(PING)b/';
if (preg_match($ping, $line)) {
fputs ($fp, «PONG :».$irc_server[«name»].«n»);
}
else {
// Флаг для определения того что бот уже нашел ключевую фразу
$stop=0;
// Запускаем цикл по всем нужным каналам
while ((list($keys, $values)=each($joined)) && !$stop) {
// Проверяем не время ли сказать чего-нибудь в канал
$str_=is_time($keys);
fputs($fp, $str_);
// Проверка на вход кого-либо на канал
if (preg_match($joined[$keys], $line, $matches)) {
$stop=1;
if ($matches[1]!=$bot[«nick»]) {
// Пишем в лог
$log_file='/home/myaccount/irc_log/#'.
$matches[2].date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("[%s] * <%s> вошел на канал %sn",
date(«H:i»$matches[1], $matches[2]));
fclose ($lf);
// Получаем строку приветствия
$str_=hello($matches);
// Отсылаем фразу серверу
fputs ($fp, $str_);
}
}
else {
// если это просто фраза в канал, либо в приват боту
if (!preg_match($action[$keys], $line, $matches)
&& (preg_match($privmsg[$keys], $line, $matches)
|| preg_match($private_me, $line, $matches))) {
$stop=1;
// Отсылаем функции обработки сообщений
// не рассматривается (в данной статье)
$str_=analyze_msg($matches);
// Выводим результат
fputs ($fp, $str_);
}
else {
// Если пользователь покинул канал
if (preg_match($partmsg[$keys], $line, $matches)) {
// Просто пишем в лог
$log_file='/home/myaccount/irc_log/#'.
$matches[2].date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("[%s] * <%s> покинул канал %sn",
date(«H:i»), $matches[1], $matches[2]));
fclose ($lf);
}
else {
// Если пользователь покинул сервер
if (preg_match($quitmsg, $line, $matches)) {
// Просто пишем в лог
$log_file='/home/myaccount/irc_log/#'.
$keys.date(«Ymd»).'.log';
$textq='';
// Определяем причину выхода (если указана)
if (trim($matches[2])!='') {
$textq="(".trim($matches[2]).")";
}
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf(
"[%s] * <%s> покинул IRC сервер %sn",
date(«H:i»), $matches[1], $textq));
fclose ($lf);
}
else {
// Если пользователь сменил ник
if (preg_match($nick, $line, $matches)) {
// Пишем в лог
$log_file='/home/myaccount/irc_log/#'.
$keys.date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("[%s] * %s взял имя %sn",
date(«H:i»),
$matches[1],
$matches[2]));
fclose ($lf);
}
else {
// Если установлена тема
if (preg_match($topic[$keys],$line,$matches)) {
// Пишем в лог
$log_file='/home/myaccount/irc_log/#'.
$keys.date(«Ymd»).'.log';
$lf=fopen($log_file,«a»);
fputs ($lf, sprintf("............",
date(«H:i»),
$matches[1],
$matches[3]));
fclose ($lf);
}
}
}
}
}
}
}
}
// Пауза 1 сек. — чтобы меньше грузить сервер
sleep(1);
}
// Закрытие сокета
fclose ($sp);
?>




Ключевые моменты

Все отсылаемые команды должны обязательно заканчиваться символом конца строки «n».

Файлы в которых храняться варианты приветствий и пр. — просто текстовые файлы, каждая отдельная фраза с новой строки. На эти файлы у пользователя под которым запускается бот должны минимально быть права на чтение.

Заключение

Вот вроде бы и все… Разбирая фразы — можно добавлять боту любую свою логику… В ближайшее время намереваюсь выложить скрипт для форматирования логов в HTML, хотя… надо ли? Это настолько очевидно…
Нравится
Не нравится

Комментарии

Нет комментариев. Ваш будет первым!