Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Show Off Your Open Source Projects (http://www.programmingforums.org/forum52.html)
-   -   Step-By-Step IRC Bot (http://www.programmingforums.org/showthread.php?t=10356)

Mocker Jun 14th, 2006 7:11 AM

Step-By-Step IRC Bot
 
I'm just getting back into perl, been a long while since I last messed with it (3+ years?). I'm starting with a simple IRC bot, that will slowly add on more features, so follow along if you want some very basic samples (and point out where I totally suck if you want :( )


First, mockerbot v 0.0000001 - using Net::IRC, connects to an IRC server, joins a channel and logs each message to a text file.
(currently 'thegup' on #programmingforums)
*lots of the code was found on O'reilly IRC hacks

Import Net::IRC, declare our variables and create the IRC connection object
:

!/usr/bin/perl -w

use Net::IRC;
use strict;
# create the IRC object
my $irc = new Net::IRC;
#log directory
my $logdir = "/home/thegupst/public_html/pfo_irc/";
my @thedate = localtime();
my $speaker = "";
my $quoted = "";
my $isSysMsg = 0;
# Create a connection object
my $conn = $irc->newconn(
        Server          => shift || 'irc.freenode.net',
        Port            => shift || '8001',
        Nick            => 'thegup',
        Ircname        => 'grlgrlgrl',
        Username        => 'humbug'
);
$conn->{channel} = shift || '#programmingforums';


Open a file in append mode based on the log path and the date, the write in the message with html blocks based upon a regular message, part or join (this will be switched to a db query later)

:

sub log_msg {
#open file and log the message
@thedate = localtime();
my $linein = "<font color=black><i>(" . $thedate[2] . ":" . $thedate[1] . ":" . $thedate[0] . ")</i></fon$
my $filename = $logdir . $thedate[4]  . $thedate[3] . $thedate[5]  . '.html';
open(LOGIN, ">>$filename");
flock (LOGIN,2);
if ($quoted eq 'dundundunleaves') {
$linein .= 'green';
$quoted = "sadly leaves";
}
if ($quoted eq 'dundundunjoins') {
$linein .= 'red';
$quoted = "joyfully enters";
}
if ($quoted ne 'joyfully enters' && $quoted ne 'sadly leaves') {
$linein .= 'blue';
}
$linein .= "'><b>" . $speaker . "</b> : " . $quoted . "</font><br>";
print LOGIN "$linein\n";
close(LOGIN);
}


The next few are the handlers for specific events you'll see towards the bottom. Net::IRC has a huge list of events you can specific specific actions for
:

sub on_privmsg {
  #message was seen in channel, parse, log and react
        my ($conn, $event) = @_;
 $isSysMsg = 0;
 $speaker = $event->{nick};
 $quoted = $event->{args}[0];
 log_msg();

}

sub on_connect {

        # shift in our connection object that is passed automatically
        my $conn = shift;
        # when we connect, join our channel and greet it
        $conn->join($conn->{channel});
        $conn->privmsg($conn->{channel}, 'Hello everyone!');
        $conn->{connected} = 1;
}

sub on_join {

        # get our connection object and the event object, which is passed
        # with this event automatically
        my ($conn, $event) = @_;

        # this is the nick that just joined
        my $nick = $event->{nick};
 $isSysMsg = 1;
 $speaker = $nick;
$quoted = "dundundunjoins";
log_msg();
}


sub on_part {

        # pretty much the same as above

        my ($conn, $event) = @_;

        my $nick = $event->{nick};
        $isSysMsg = 1;
        $speaker = $nick;
        $quoted = "dundundunleaves";
        log_msg();
}


Next we add handlers to the connection object, so it knows what function to call when a certain event happens. A list of events are in perldoc Net::IRC::Events
Once all that is setup, start the IRC connection, which puts it in a loop and leaves the action up to the handlers
:

# add event handlers for join and part events
$conn->add_handler('join', \&on_join);
$conn->add_handler('part', \&on_part);
$conn->add_handler('public', \&on_privmsg);

# The end of MOTD (message of the day), numbered 376 signifies we've connect
$conn->add_handler('376', \&on_connect);
# start IRC
$irc->start();


Mocker Jun 14th, 2006 7:15 AM

pfo_current
 
Now I'll show the quick cgi script I made that sits on my server and displays the #programmingforums log for anyone that is curious

pfo_current.cgi

Set the values I'll be using -
:

#!/usr/bin/perl

my $logdir = "/home/thegupst/public_html/pfo_irc/";
my @thedate = localtime();
my $thetime = localtime();
my $filename = $logdir . $thedate[4]  . $thedate[3] . $thedate[5]  . ".html";


open todays log, print out the html headers and current time (so people know what the timestamps refer to), then print out each line of the file and close it
:

open(FILEREAD, "< $filename") || die "could not open file";
print "Content-type: text/html\r\n\r\n";
print "<html><head><title>#programmingforums chat log</title></head><body>";
print "<b>#programmingforums current logs. Local time: " . $thetime . "</b><br><hr><br>";
while (<FILEREAD>) {
my $line = $_;
chomp($line);
print "$line\n";
}
close(FILEREAD);
print "</body></html>";


aaannd done. Short and simple. It is online at
http://thegupstudio.com/cgi-bin/pfo_current.cgi

Mocker Jun 15th, 2006 1:35 PM

IRC Relay
 
Feel like I'm talking to myself,wee. Added a new relay feature. The bot now opens a socket that it monitors. Messages sent to that socket get relayed to the channel the bot is in.

Currently they relayed by a cgi-script. The pfo one is http://thegupstudio.com/cgi-bin/pforelay.cgi

First pforelay.pl , the bot itself. Made some changes to the top.
*note, the code below is not complete. Just snippets with the biggest changes*

Took off strict to make it easier for subs like the socket handler to use the $conn object. Added and initialized the socket. Set some random variables.

:

require "cgi-lib.pl";
use Net::IRC;
#use strict;
use IO::Socket::INET;

my $MySocket = new IO::Socket::INET->new(LocalPort=>2345,Proto=>'udp');
my $irc = new Net::IRC;

my @thedate = localtime();
my $logdir = "/home/thegupst/public_html/pfo_irc/";
#my @messagein;
my $messagefrom = "";
my $inputstring = "";
my $lastsec = (localtime)[0]; #current seconds
my $lastmin = (localtime)[1]; #current min


logging and other functions are the same. Added the function to handle socket messages. Pretty basic stuff. Sets a time recieved. The message format is sent as username~message and stuff here , so I split it on ~, with the downside this would get messed up if that character is in the username or message. If there is less than 5 seconds between recieved messages it doesn't do anything to prevent flooding or spamming

:

sub on_sockmsg {
#parse, send to irc
my $text = "";
my $cursec = (localtime)[0];
my $curmin = (localtime)[1];
$MySocket->recv($text,128);
my @msgin = split('\~', $text);
$text = "from " . $msgin[0] . " : " . $msgin[1];
if (($curmin == $lastmin) && (($cursec - $lastsec) < 5)){
 #spamspam
 }
else {
$conn->privmsg($conn->{channel}, $text );
$lastmin = $curmin;
$lastsec = $cursec;
$speaker = "m_" . $msgin[0];
$quoted = $msgin[1];
log_msg();
}
}


Then the only other difference really is adding a file(and socket) handler to to the irc object so it knows what to do if it gets a message
:

$irc->addfh($MySocket, \&on_sockmsg);



**pforelay.cgi**
Now for the cgi script that displays the last 15 messages and relays anything you enter to the bot

left out some random declarations. Sets the log file path, and also it uses the 'tail' command to get the last 15 entries and the filepath it stores those lines at. Then sets up the socket so we can send stuff to the bot.
:

require "cgi-lib.pl";
use IO::Socket::INET;
my $logdir = "/home/thegupst/public_html/pfo_irc/";
my $filename = $logdir . $thedate[4]  . $thedate[3] . $thedate[5]  . ".html";
my $tmptail = $logdir . "tmptail";
my $tailcmd = "tail -15 " . $filename . " > " . $logdir . "tmptail";
&ReadParse(*input);
$MySocket=new IO::Socket::INET->new(PeerPort=>2345,Proto=>'udp',PeerAddr=>'localhost');


If the script is sent any data, send it to the bot
:

if (length($input{'ircmsg'}) > 1) {
#send message to socket
$inputstring = $input{'ircfrom'} . "~" . $input{'ircmsg'};
$MySocket->send($inputstring) || die "could not send";
}


The rest just prints outthe html to the form. This is the code to read the last 15 lines of todays log
:

system($tailcmd) == 0 || die "could not tail: $?";
open(TAILIN, $tmptail);
while(<TAILIN>){
my $line = $_;
chomp($line);
print "$line\n";
}
close(TAILIN);


hope this is helpful/interesting or something. Feel free to use the url http://thegupstudio.com/cgi-bin/pforelay.cgi to check out #programmingforums irc channel (i'm kimochii on irc)

lostcauz Jun 15th, 2006 1:50 PM

Nice work! Now to align the date/time, catch actions and maybe add some stats. :)

Pizentios Jun 15th, 2006 2:26 PM

yeah stats would be cool also

Mocker Jun 18th, 2006 11:08 AM

MockerBot going on
 
Woo, made a bunch of changes to my bot at work last night (slow night eh).

Some of the new things
----------
*Logs everything to a mysql database. Messages are logged with usernaem, userid, current datetime, and type. Usernames keep info for each user it has recorded as posting. Right now it just has a postcount, but there are fields for passwords and more info so it could serve as kinda a nickserv bot and keep profiles and authentic logged messages etc.
*Added a bunch of commands! right now there is
!quote random - displays a random quote from a quote file
!quote add your quote here - adds the quote to the quote file
!quote randomirc - displays a random entry in the irc logs
!quote randomirc username - displays a random entry in the irc logs from that username
!info postcount username - number of entries by that username


It is 263 lines now so I put it in the pastebin. umm.. i'm feelnig lazy so I'll just link that. I'll break down the sections again later, shouldn't be too hard to follow. There are pretty messy areas because I'm learning perl as I go again and never used the DBI module.
http://pfo.pastebin.com/716502
This is the exact script (except for db info) used for mockerbot, currently on #programmingforums irc channel.

Next up is adding a way to track the current userlist, and then working on the relay script so you can see all the info and commands from there.

*no comments on my variable names :( global $data1 and $data2 are horrible I know I know

Mocker Jul 11th, 2006 7:20 AM

Woo, so I've added number of new things to my bot. I actually still need to update the help file so I'll try to list em all here for now

Logs the chat to a mysql database and keeps a postcount for each username
Has a relay you can chat through http://thegupstudio.com/cgi-bin/pforelay.cgi
You can search and view logs http://thegupstudio.com/cgi-bin/pfo_current.cgi
Keeps an additional quote db that is added to manually

The current commands are-
!info postcount (LIKE) username
!quote random|randomirc (LIKE) (username) - random uses quotedb, randomirc is random line from the log
!help - pm's a brief help file
!talk on|off (username)
!define (DIC) word - users dict.org dictionaries, currently available :
SPANISH ROM GERMAN FRENCH NLD DEVIL JARGON THES default is spanish
!tran (LANG) phrase - uses google translate or freedict.com to translate
GERMAN FRENCH ITALIAN ARABIC JAPANESE NIHONGO KOREAN CHINESE DEUTSCH FRANCAIS ESPANOL SPANISH(default). The native languages translate back to english
!google phrase - gets first google result

annd, the full source code is available. Kimobot runs on #programmingforums irc room (irc.freenode.net) if you want to test it.

http://thegupstudio.com/pfo_irc/ircbot.html

Let me know what you think :) I'm going to just keep adding to it, probably group the functions into modules. If you are interested in trying to use the source I'll include how I've setup the mysql tables and other files it uses.

Mocker Jul 15th, 2006 9:27 AM

Slow night at work, got three more functions added to the bot

the first isnt really part of the bot, it is a quick php script that uses the GD library to generate an image of 3 random lines from irc
http://thegupstudio.com/pfo_irc/randomirc_img2.php - refresh it a few times
it is an image so you could do <img src="..etc"> and have it displayed
it also takes 2 arguments, lines and names. http://thegupstudio.com/pfo_irc/rand...names=kimochii for example

The next bot command is
!random list of items here etc etc
or
!random number TO number
The first way just choose a random item that comes after the command
the second is a random int between the two numbers

And this one took me a while, there is now a 'game' you can play
!play quoteirc ON|OFF to turn the game on and off
it will display a random irc quote and the first person to say the name of the speaker wins that round, it then adds a score and goes to the next one
!skip - skips current round, displays answer and goes to the next
!score - displays current score, works while game is off for last played game. New games reset the score

If you dont skip and dont guess the name it will just wait until you do or you end the game

I've had all of no interest in this so I probably will not post the source here anymore. Its still available, i'm just too lazy to update it.


All times are GMT -5. The time now is 8:01 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC