Logical-Portal — молодежный портал со своей изюминкой, каждый сможет найти что-то интересное и полезное для себя, например наш форум общения с которым вам обязательно нужно ознакомиться.
Установил я значит как-то IRC сервер и радовались пользователи... Но захотели пользователи иметь возможность еще и логи читать (слово ж не воробей!).
Первым, что пришло в голову - запустить на сервере mIRC на постоянную и с него вести логи нужных каналов. Сказано - сделано. Однако не совсем удобно - постоянно держать законектеным mIRC. Вот тогда и зародилась идея о боте...
До этого я с IRC (и с ботами тем более) особо не сталкивался и потому пошел сразу в гугел. После 5 страниц полного бреда (как по моему) я нашел одну статейку (жаль урл не помню!) в которой была на мой взгляд самая что ни на есть ключевая фраза: "чтобы понять как общаются сервер и клиент можно включить в обычном mIRC опцию /debug on и все данные будут сохранятся в лог-файле". Дальше я не читал... Включив опцию, активно пообщавшись - за 2 часа у меня было достаточно информации.
// Функция из мана используемая для получения "наиболее случайных чисел" 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, хотя... надо ли? Это настолько очевидно...
Установил я значит как-то IRC сервер и радовались пользователи... Но захотели пользователи иметь возможность еще и логи читать (слово ж не воробей!).
Первым, что пришло в голову - запустить на сервере mIRC на постоянную и с него вести логи нужных каналов. Сказано - сделано. Однако не совсем удобно - постоянно держать законектеным mIRC. Вот тогда и зародилась идея о боте...
До этого я с IRC (и с ботами тем более) особо не сталкивался и потому пошел сразу в гугел. После 5 страниц полного бреда (как по моему) я нашел одну статейку (жаль урл не помню!) в которой была на мой взгляд самая что ни на есть ключевая фраза: "чтобы понять как общаются сервер и клиент можно включить в обычном mIRC опцию /debug on и все данные будут сохранятся в лог-файле". Дальше я не читал... Включив опцию, активно пообщавшись - за 2 часа у меня было достаточно информации.
// Функция из мана используемая для получения "наиболее случайных чисел" 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, хотя... надо ли? Это настолько очевидно...
Подпись пользователя: У гениальности есть побочные эффекты