#!/usr/bin/perl #------------------------------------------------------------+ # A simple IRC Information Bot in PERL | # Rick Saunders - Dec 19, 2002 | # Version 1.1 | # | # Use and distribute freely | #------------------------------------------------------------+ #*************************Bot Setup*************************** use strict; # Set global variables use vars qw ( $version $build_date $channels_to_join $server $port $botnick $botuser $bottext $filename $unlockdb $dbh $sock @channels ); $version = "v1.1"; $build_date = "Dec 19, 2002"; $server = ""; $port = "6667"; $channels_to_join = ""; $botnick = "Inductor"; $botuser = "inductor"; $bottext = "A simple PERL information bot"; $filename = "chanlog1.dta"; $unlockdb = 0; my $database = 1; # 1 = PostgreSQL 0 = MySQL use IO::Socket; use DBI; # I've used the example user 'guest' with password 'guest' # here. You'll probably want to change this if ( $database ) { $dbh = DBI->connect( 'dbi:Pg:dbname=info', 'guest', 'guest' ); } else { $dbh = DBI->connect( 'dbi:mysql:dbname=info', 'guest', 'guest' ); } # Run the script once to set your username and password # The script will re-write itself commenting out the line # below. &initialize; #-----------------No need to edit below--------------------- my $line; my $stime = time; my $lastnick = ""; $channels_to_join = uc( $channels_to_join ); &cleanup; # Cleanup the access lists &startbot; # Start the bot and handle housekeeping while ($line = <$sock>) { my $flooding = 0; my $hostname; # Respond to pings immediately my ($command, $text) = split(/ :/, $line); if ($command eq 'PING') { while ( (index($text,"\r") >= 0) || (index($text,"\n") >= 0) ) { chop($text); } print $sock "PONG $text\n"; next; } # Split out normal IRC traffic my ($nick,$type,$channel) = split(/ /, $line); chomp $channel; ($nick,$hostname) = split(/!/, $nick); $nick =~ s/://; $text =~ s/://; $/ = "\r\n"; while($text =~ m#$/$#){ chomp($text); } # Load the startup nicks into the 'onchan' table if ( $command =~ / 353 / ) { my @crow = split( / /, $command ); my $tchan = uc( $crow[ $#crow ] ); my @cnicks = split( / /, $text ); foreach my $tnick ( @cnicks ) { $tnick = uc( $tnick ); $tnick =~ s/\@//g; chomp $tnick, $tchan; my $uth = $dbh->prepare( "select * from onchan where channel = '$tchan' and nick = '$tnick' " ); $uth->execute; if ( ! $uth->rows ) { my $vth = $dbh->prepare( "insert into onchan ( channel, nick, joined ) values ( '$tchan', '$tnick', now() )"); $vth->execute; } else { my $vth = $dbh->prepare( "update onchan set joined = now(), parted = null where nick = '$tnick' and channel = '$tchan' " ); $vth->execute; } &checknotes( $tnick ); } } # Rudimentary flood control. If two commands come in from the same # nick within 2 seconds then the second and subsequent are ignored if ( ( $nick eq $lastnick ) && ( $nick !~ /$server/i ) && ( index( $text, "!" ) == 0 ) ) { if ( time - $stime < 2 ) { $flooding = 1; } $stime = time; } $lastnick = $nick; next if $flooding; #loop if being flooded # Answer a 'hi' message#---------------------------------------------------------------- if( ( $text =~ /hi $botnick/gi ) || ( $text =~ /hello $botnick/gi ) ){ print $sock "PRIVMSG $channel :hi $nick\n"; } # Handle /Nick /Part and /Quit if ( $type eq "NICK" ) { &handlenick( uc ( $text ), uc( $nick ) ); } elsif ( $type eq "PART" ) { &handlepart( uc( $text ), uc( $nick ) ); } elsif ( $type eq "QUIT" ) { &handlequit( uc( $nick ) ); } elsif ( $type eq "JOIN" ) { &handlejoin( uc( $nick ) ); } # Handle messages sent to the bot either by /msg format in # which case we call 'answerprivate' or on channel in which # case we call 'answerchan' elsif ( $type eq "PRIVMSG" ) { if ( $channel eq $botnick ) { &answerprivate( $text, $nick ); } else { my $uchannel = uc( $channel ); my $unick = uc( $nick ); chomp( $uchannel, $unick ); my $slantedtext = $text; $slantedtext =~ s/\'/\\\'/g; my $ath = $dbh->prepare( "insert into chanlog values ( now(), '$uchannel', '$unick', '$slantedtext' )" ); $ath->execute; if ( index( $text, "!" ) == 0 ) { &answerchan( $text, $channel, $nick ); } } } } #---------------- Handle NiCK, PART, and QUIT ------------------- # If a user is authenticated and changes nicks... track the new nick #---------------------------------------------------------------- sub handlenick { my $newnick = shift; my $nick = shift; my $now = gmtime( time ); chomp( $newnick, $nick ); my $tth = $dbh->prepare( "update users set newnick = '$newnick' where upper( newnick ) = '$nick' " ); $tth->execute; $tth = $dbh->prepare( "update onchan set nick = '$newnick' where nick = '$nick' " ); $tth->execute; } # If a logged in user parts a channel remove his access from the channel #---------------------------------------------------------------- sub handlepart { my $channel = shift; my $nick = shift; chomp $channel; my $now = gmtime( time ); my $tth = $dbh->prepare( "select n.buser, a.channel from users n, authedchans a where n.newnick = '$nick' and a.buser = upper( n.buser ) and upper( a.channel ) = '$channel'" ); $tth->execute; if ( $tth->rows ) { my ( $tnick, $channel ) = $tth->fetchrow_array; $tnick =~ s/ +$//g; $tth = $dbh->prepare( "delete from authedchans where buser = '$tnick' and channel = '$channel'" ); $tth->execute; &printlog( "$now:$nick removed from access on $channel due part\n" ); } $tth = $dbh->prepare( "update onchan set joined = null, parted = now() where nick = '$nick' and channel = '$channel'" ); $tth->execute; close OUT; } # If a logged in user quits... remove him from all channels #---------------------------------------------------------------- sub handlequit { my $nick = shift; my $now = gmtime( time ); my $tth = $dbh->prepare( "select buser from users where newnick = '$nick'" ); $tth->execute; if ( $tth->rows ) { my ( $user ) = $tth->fetchrow_array; $user =~ s/ +$//g; $tth = $dbh->prepare( "delete from authedusers where buser = '$user'" ); $tth->execute; $tth = $dbh->prepare( "delete from authedchans where buser = '$user'" ); $tth->execute; &printlog( "$now:$nick removed from all access due quit\n" ); } $tth = $dbh->prepare( "update onchan set joined = null, parted = now() where nick = '$nick' " ); $tth->execute; close OUT; } # If someone joins... #---------------------------------------------------------------- sub handlejoin { my $nick = shift; my $tth = $dbh->prepare( "select * from notes where tonick = '$nick'" ); $tth->execute; if ( $tth->rows ) { &checknotes( $nick ); } } #----------------- Handle Administrative Commands -------------- # All these commands must be sent using /msg botnick !command sub answerprivate { my $text = shift; my $nick = shift; $text =~ s/ +$//g; $text =~ s/\s+/ /g; $nick =~ s/ +$//g; my @row = split( / /, $text ); my $botcommand = uc( $row[0] ); if ( $botcommand eq "!LOGIN" ) { if ( $#row == 3 ) { &login( uc( $row[1] ), $row[2], uc( $row[3] ), uc( $nick ) ); } else { print $sock "NOTICE $nick :!LOGIN \n"; } } elsif ( $botcommand eq "!LOGOUT" ) { &logout( uc( $nick ) ); } elsif ( $botcommand eq "!ADDUSER" ) { if ( $#row == 3 ) { &adduser( uc( $row[1] ), uc( $row[2] ), $row[3], $nick ); } else { print $sock "NOTICE $nick :!ADDUSER \n"; } } elsif ( $botcommand eq "!REMUSER" ) { if ( $#row == 1 ) { &remuser( uc( $row[1] ), $nick ); } else { print $sock "NOTICE $nick :!REMUSER \n"; } } elsif ( $botcommand eq "!CHGPASS" ) { if ( $#row == 3 ) { &chgpass( uc( $row[1] ), $row[2], $row[3], $nick ); } else { print $sock "NOTICE $nick :!CHGPASS \n"; } } elsif ( $botcommand eq "!RESTART" ) { &restart( $nick ); } elsif ( $botcommand eq "!KILLBOT" ) { &killbot( $nick ); } elsif ( $botcommand eq "!LISTUSERS" ) { &listusers( $nick ); } elsif ( $botcommand eq "!OP" ) { if ( $#row == 1 ) { &op( $nick, uc( $row[1] ) ); } else { print $sock "NOTICE $nick :!OP \n"; } } elsif ( $botcommand eq "!LISTAUTHED" ) { &listauthed( $nick ); } elsif ( $botcommand eq "!ADDCHAN" ) { if ( $#row == 2 ) { &addchan( uc( $row[1] ), uc( $row[2] ), $nick ); } else { print $sock "NOTICE $nick :!ADDCHAN \n"; } } elsif ( $botcommand eq "!REMCHAN" ) { if ( $#row == 2 ) { &remchan( uc( $row[1] ), uc( $row[2] ), $nick ); } else { print $sock "NOTICE $nick :!REMCHAN \n"; } } elsif ( $botcommand eq "!REAUTH" ) { if ( $#row == 2 ) { &reauth( uc( $row[1] ), uc( $row[2] ), $nick ); } else { print $sock "NOTICE $nick :!REAUTH \n"; } } elsif ( $botcommand eq "!GETUSER" ) { if ( $#row == 1 ) { &getuser( uc( $row[1] ), $nick ); } else { print $sock "NOTICE $nick :!GETUSER \n"; } } elsif ( $botcommand eq "!LOCK" ) { &lock( $nick ); } elsif ( $botcommand eq "!UNLOCK" ) { &unlock( $nick ); } elsif ( $botcommand eq "!SPEAK" ) { if ( $#row > 1 ) { &speak( $text, $nick ); } else { print $sock "NOTICE $nick :!SPEAK \n"; } } elsif ( $botcommand eq "!NOTE" ) { if ( $#row > 1 ) { my $note = ""; for my $a ( 2..$#row ) { $note .= $row[$a]; } &sendnote( uc( $nick ), uc( $row[1] ), $note ); } else { print $sock "NOTICE $nick :!NOTE \n"; } } } # Log yourself in... requires username, password, channels (or all) #---------------------------------------------------------------- sub login { my $user = shift; my $password = shift; my $xchan = shift; my $nick = shift; my $now = gmtime( time ); if ( &checkpass( $user, $password ) ) { my $wth = $dbh->prepare( "select buser from authedusers where buser = '$user'" ); $wth->execute; if ( ! $wth->rows ) { my $xth = $dbh->prepare( "insert into authedusers values ( '$user' )" ); $xth->execute; $xth = $dbh->prepare( "update users set newnick = '$nick' where buser = '$user' "); $xth->execute; if ( $xchan eq "ALL" ) { $xth = $dbh->prepare( "select buser, channel from channels where buser = '$user' " ); } else { $xth = $dbh->prepare( "select buser, channel from channels where buser = '$user' and channel = '$xchan' " ); } $xth->execute; while( my @row = $xth->fetchrow_array ) { my ( $cuser, $cchan ) = @row; $cuser =~ s/ +$//g; my $yth = $dbh->prepare( "insert into authedchans values ( '$cuser', '$cchan' )" ); $yth->execute; } if ( $xchan eq "ALL" ) { print $sock "NOTICE $nick :$nick is authenticated on all channels!\n"; &printlog( "$now:$nick logged in global\n" ); } else { print $sock "NOTICE $nick :$nick is authenticated on $xchan!\n"; &printlog( "$now:$nick logged in on $xchan\n" ); } $xth = $dbh->prepare( "update users set logintime = now() where upper( buser ) = '$user' " ); $xth->execute; } else { print $sock "NOTICE $nick :$nick - you are already authenticated!\n"; } } else { print $sock "NOTICE $nick :$nick password rejected!\n"; &printlog( "$now:$nick failed authenticate\n" ); } close OUT; } # Log yourself out (you can't logout someone else) #---------------------------------------------------------------- sub logout { my $nick = shift; my ( $now, $vth, $xth, $user ); $now = gmtime( time ); my $vth = $dbh->prepare( "select buser from users where newnick = '$nick'" ); $vth->execute; if ( $vth->rows ) { ( $user ) = $vth->fetchrow_array; $user =~ s/ +$//g; $xth = $dbh->prepare( "delete from authedusers where buser = '$user'" ); $xth->execute; $xth = $dbh->prepare( "delete from authedchans where buser = '$user'" ); $xth->execute; $xth = $dbh->prepare( "update users set newnick = NULL where buser = '$user' " ); $xth->execute; print $sock "NOTICE $nick :User $nick logged out\n"; &printlog( "$now:$nick logged out\n" ); } else { print $sock "NOTICE $nick :Access denied\n"; &printlog( "$now:$nick attempted logout - denied\n" ); } close OUT; } # Add a user to the bot. Requires username, channels (or all) and # whether the user is an admin [0|1] #---------------------------------------------------------------- sub adduser { my $user = shift; my $xchan = shift; $xchan =~ s/ //g; my $admin = shift; my $nick = shift; my ( $now, $authed, $centry, $newpass, $password, $sth, @cchans ); $now = gmtime( time ); if ( &authadmin( $nick ) ) { $password = join '', ( 0..9, 'A'..'Z', 'a'..'z')[rand 62, rand 62, rand 62, rand 62, rand 62, rand 62, rand 62, rand 62]; $sth = $dbh->prepare( "select buser from users where upper( buser ) = '$user' " ); $sth->execute; if ( ! $sth->rows ) { $newpass = &createpass( $password ); $sth = $dbh->prepare( "insert into users values ( now(), '$user', '$newpass', NULL, $admin )" ); $sth->execute; if ( $xchan eq 'ALL' ) { foreach $centry ( @channels ) { if ( $centry =~ /\#/ ) { $sth = $dbh->prepare( "insert into channels values( '$user', '$centry' ) " ); $sth->execute; } } } else { @cchans = split( /\,/, $xchan ); foreach $centry ( @cchans ) { if ( $centry ne "" ) { $sth = $dbh->prepare( "insert into channels values( '$user', '$centry' ) " ); $sth->execute; } } } print $sock "NOTICE $nick :User $user added with password $password!\n"; print $sock "NOTICE $nick :Remind user to use !chgpass to change this.\n"; &printlog( "$now:$nick added user $user\n" ); } else { print $sock "NOTICE $nick :User already exists!\n"; } } else { print $sock "NOTICE $nick :User addition denied, no priviledge!\n"; &printlog( "$now:$nick attempted user addition for $user - failed\n" ); } close OUT; } # Remove a user from access to the bot #---------------------------------------------------------------- sub remuser { my $user = shift; my $nick = shift; my ( $now, $wth, $authed ); $now = gmtime( time ); if ( &authadmin( $nick ) ) { $wth = $dbh->prepare( "delete from authedusers where buser = '$user' " ); $wth->execute; $wth = $dbh->prepare( "delete from users where buser = '$user'" ); $wth->execute; $wth = $dbh->prepare( "delete from channels where buser = '$user' " ); $wth->execute; $wth = $dbh->prepare( "delete from authedchans where buser = '$user' " ); $wth->execute; print $sock "NOTICE $nick :User removed from all channels!\n"; &printlog( "$now:$nick removed user $user\n" ); } else { print $sock "NOTICE $nick :User removal denied, no priviledge!\n"; &printlog( "$now: $nick attempted to remove $user - failed\n" ); } close OUT; } # Change your password. Requires userid, oldpass and newpass #---------------------------------------------------------------- sub chgpass { my $user = shift; my $oldword = shift; my $newword = shift; my $nick = shift; my ( $now, $authed, $sth ); $now = gmtime( time ); if ( &authuser( $nick ) ) { if ( length( $newword ) < 8 ) { print $sock "NOTICE $nick :Not enough letters in password!\n"; } else { $authed = &checkpass( $user, $oldword ); if ( $authed ) { my $newpass = &createpass( $newword ); $sth = $dbh->prepare( "update users set password = '$newpass' where buser = '$user'" ); $sth->execute; print $sock "NOTICE $nick :Password Changed!\n"; &printlog( "$now:$nick changed password!\n" ); } else { print $sock "NOTICE $nick :Old password rejected\n"; } } } else { print $sock "NOTICE $nick :User addition denied, no priviledge!\n"; &printlog( "$now:$nick attempted password change for $user - failed\n" ); } close OUT; } # Restart the bot #---------------------------------------------------------------- sub restart { my $nick = shift; my $now = gmtime( time ); if ( &authadmin( $nick ) ) { print $sock "NOTICE $nick :Restart called - dropping connection\n"; &printlog( "$now:$nick called reconnect. Dropping connection\n" ); undef $sock; sleep 10; &cleanup; &startbot; } else { print $sock "NOTICE $nick :Reconnect not allowed - no priviledge!\n"; &printlog( "$now:$nick attempted reconnect - denied!\n" ); } close OUT; } # Kill the bot #---------------------------------------------------------------- sub killbot { my $nick = shift; my $now = gmtime( time ); if ( &authadmin( $nick ) ) { print $sock "NOTICE $nick :KILL called... shutting down bot!\n"; &printlog( "$now:$nick called KILL. Dropping connection\n" ); exit; } else { print $sock "NOTICE $nick :Kill not allowed - no priviledge!\n"; &printlog( "$now:$nick attempted KILL - denied!\n" ); } close OUT; } # List the users that have access to the bot #---------------------------------------------------------------- sub listusers { my $nick = shift; my $now; if ( &authuser( $nick ) ) { print $sock "NOTICE $nick :User -> Current Nick (if empty not logged in )\n"; print $sock "NOTICE $nick :----------------------------------------------\n"; my $uth = $dbh->prepare( "select buser, newnick from users" ); $uth->execute; while( my @row = $uth->fetchrow_array ) { my ( $name, $currentnick ) = @row; $name =~ s/ +$//g; $currentnick =~ s/ +$//g; $name = sprintf( "%-20.20s", $name ); print $sock "NOTICE $nick :$name -> $currentnick\n"; sleep 2; } } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted user list - denied\n" ); } close OUT; } # List the users currently logged in #---------------------------------------------------------------- sub listauthed { my $nick = shift; my ( $now, $authed, $uth, @row ); $now = gmtime( time ); if ( &authuser( $nick ) ) { print $sock "NOTICE $nick :User -> Current Nick -> Channel -> Logged in\n"; print $sock "NOTICE $nick :----------------------------------\n"; $uth = $dbh->prepare( "select a.buser, n.newnick, n.logintime, a.channel from authedchans a, users n where upper( n.buser ) = upper( a.buser )" ); $uth->execute; while( @row = $uth->fetchrow_array ) { my ( $aname, $anick, $ltime, $achan ) = @row; $aname =~ s/ +$//g; $anick =~ s/ +$//g; $aname = sprintf( "%-12.12s", $aname ); $anick = sprintf( "%-10.10s", $anick ); $achan = sprintf( "%-20.20s", $achan ); print $sock "NOTICE $nick :$aname - > $anick -> $achan -> $ltime\n"; sleep 2; } } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted authorized user list - denied\n" ); } close OUT; } # Unlock the database for regular IRC users to remove/replace #---------------------------------------------------------------- sub unlock { my $nick = shift; my $now = gmtime( time ); if ( &authuser( $nick ) ) { $unlockdb = 1; print $sock "NOTICE $nick :Database unlocked!\n"; &printlog( "$now:$nick unlocked database\n" ); } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted db unlock - denied\n" ); } close OUT; } # Relock the database #---------------------------------------------------------------- sub lock { my $nick = shift; my $now = gmtime( time ); if ( &authuser( $nick ) ) { $unlockdb = 0; print $sock "NOTICE $nick :Database locked!\n"; &printlog( "$now:$nick locked database\n" ); } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted db lock - denied\n" ); } close OUT; } # Add a registered user to a new channel #---------------------------------------------------------------- sub addchan { my $user = shift; my $newchan = shift; my $nick = shift; my $centry; my $now = gmtime( time ); $newchan =~ s/ //g; chomp $newchan; if ( &authadmin( $nick ) ) { my @cchans = split( /\,/, $newchan ); foreach $centry ( @cchans ) { if ( $centry ne "" ) { my $uth = $dbh->prepare( "select * from channels where buser = '$user' and channel = '$centry' " ); $uth->execute; if ( ! $uth->rows ) { $uth = $dbh->prepare( "insert into channels values( '$user', '$newchan' ) " ); $uth->execute; } else { print $sock "NOTICE $nick :$user alread on channel $centry\n"; } } } print $sock "NOTICE $nick :User $user added to $newchan\n"; $newchan =~ s/\,/ /g; &printlog( "$now:$user added to $newchan\n" ); } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted add channel - denied\n" ); } close OUT; } # Remove a registered user from a channel #---------------------------------------------------------------- sub remchan { my $user = shift; my $newchan = shift; my $nick = shift; my $centry; my $now = gmtime( time ); $newchan =~ s/ //gi; chomp $newchan; if ( &authadmin( $nick ) ) { my @cchans = split( /\,/, $newchan ); foreach $centry ( @cchans ) { my $uth = $dbh->prepare( "delete from channels where buser = '$user' and channel = '$centry' " ); $uth->execute; $uth = $dbh->prepare( "delete from authedchans where buser = '$user' and channel = '$centry' " ); $uth->execute; } $newchan =~ s/\,/ /g; print $sock "NOTICE $nick :User $user removed from $newchan\n"; &printlog( "$now:$user removed from $newchan\n" ); } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted remove channel - denied\n" ); } close OUT; } # Use this to reauthenticate yourself on a channel if you have # parted then rejoined. #---------------------------------------------------------------- sub reauth { my $user = shift; my $newchan = shift; my $nick = shift; my $centry; my $now = gmtime( time ); $newchan =~ s/ //g; chomp $newchan; if ( &authuser( $nick ) ) { my @cchans = split( /\,/, $newchan ); foreach $centry ( @cchans ) { my $uth = $dbh->prepare( "select * from authedchans where buser = '$user' and channel = '$centry' " ); $uth->execute; if ( ! $uth->rows ) { $uth = $dbh->prepare( "insert into authedchans values( '$user', '$centry') " ); $uth->execute; print $sock "NOTICE $nick :$user re-added to $newchan\n"; &printlog ("$now:$user re-added to $newchan\n" ); } else { print $sock "NOTICE $nick :$user already on channel $centry\n"; } } } else { print $sock "NOTICE $nick :Access denied!\n"; &printlog( "$now:$nick attempted re-add channel - denied\n" ); } close OUT; } # Get the username associated with a nick #---------------------------------------------------------------- sub getuser { my $anick = shift; my $nick = shift; my $sth; chomp $anick; if ( &authuser( $nick ) ) { $sth = $dbh->prepare( "select newnick, buser, logintime from users where newnick = '$anick'" ); $sth->execute; if ( $sth->rows ) { my ( $thisnick, $thisuser, $ltime ) = $sth->fetchrow_array; $thisnick =~ s/ +$//g; $thisuser =~ s/ +$//g; print $sock "NOTICE $nick :Username for $thisnick is $thisuser (Logged in at $ltime)\n"; } else { print $sock "NOTICE $nick :No authenticated user with nick $nick\n"; } } } # Send a message to a nick #---------------------------------------------------------------- sub sendnote { my $fromnick = shift; my $tonick = shift; my $note = shift; $note =~ s/\'/\\\'/g; my $sth; if ( &authuser( $fromnick ) ) { $sth = $dbh->prepare( "insert into notes values ( now(), '$fromnick', '$tonick', '$note' )" ); $sth->execute; print $sock "NOTICE $fromnick :Message enterred!\n"; } } # Read messages #---------------------------------------------------------------- sub readnotes { my $nick = shift; my @row; my $junk; my $sth = $dbh->prepare( "select * from notes where tonick = '$nick' " ); $sth->execute; if ( $sth->rows ) { while( @row = $sth->fetchrow_array ) { my ( $time, $from, $to, $text ) = @row; $from =~ s/ +$//g; ( $time, $junk ) = split( /\./, $time ); print $sock "NOTICE $nick :(Note from $from at $time ) -> $text\n"; sleep 1; } } } # Delete messages from the database #---------------------------------------------------------------- sub deletenotes { my $nick = shift; my $sth = $dbh->prepare( "delete from notes where tonick = '$nick' " ); $sth->execute; print $sock "NOTICE $nick :Your messages have been deleted!\n"; } # Check for messages #---------------------------------------------------------------- sub checknotes { my $nick = shift; chomp $nick; my @row; my $junk; my $sth = $dbh->prepare( "select * from notes where tonick = '$nick' " ); $sth->execute; if ( $sth->rows ) { print $sock "NOTICE $nick :You have messages. !READ to view them. !DELETE to delete them after reading.\n"; sleep 1; } } # Get the bot to say something on channel #---------------------------------------------------------------- sub speak { my $text = shift; my $nick = shift; my @row = split( / /, $text ); my $channel = $row[1]; my $speak; if ( &authuser( $nick ) ) { $speak = ""; for my $a ( 2..$#row ) { $speak .= $row[$a] . " "; } print $sock "PRIVMSG $channel :$speak\n"; } } sub op { my $nick = shift; my $chan = shift; if ( &authuser( $nick ) ) { print $sock "MODE $chan +o $nick\n"; } } #---------------- Handle In Channel Commands ---------------------- sub answerchan { my $text = shift; my $channel = shift; my $nick = shift; my @row = split( / /, $text ); my $botcommand = uc( $row[0] ); if ( $botcommand eq "!VERSION" ) { print $sock "PRIVMSG $channel :Ozzzy's PERL IRC Infobot $version $build_date\n"; } elsif ( $botcommand eq "!INFO" ) { if ( $#row == 1 ) { &info( uc( $row[1] ), $channel ); } else { print $sock "PRIVMSG $channel :!INFO \n"; } } elsif ( $botcommand eq "!ADD" ) { if ( $#row > 1 ) { &add( $text, $channel, $nick ); } else { print $sock "PRIVMSG $channel :!ADD \n"; } } elsif ( $botcommand eq "!REPLACE" ) { if ( $#row > 1 ) { &replace( $text, $channel, $nick ); } else { print $sock "PRIVMSG $channel :!REPLACE \n"; } } elsif ( $botcommand eq "!REMOVE" ) { if ( $#row == 1 ) { &remove( $text, $channel, $nick ); } else { print $sock "PRIVMSG $channel :!REMOVE \n"; } } elsif ( $botcommand eq "!SEEN" ) { if ( $#row == 1 ) { &seen( uc( $row[1] ), uc( $channel ) ); } else { print $sock "PRIVMSG $channel :!SEEN \n"; } } elsif ( $botcommand eq "!LAST" ) { if ( $#row == 1 ) { &lastentry( uc( $row[1] ), uc( $channel ) ); } else { print $sock "PRIVMSG $channel :!LAST \n"; } } elsif ( $botcommand eq "!HELP" ) { &help( $channel ); } elsif ( $botcommand eq "!READ" ) { &readnotes( uc( $nick ) ); } elsif ( $botcommand eq "!DELETE" ) { &deletenotes( uc( $nick ) ) ; } } # Query the database #---------------------------------------------------------------- sub info { my $testkey = shift; my $channel = shift; $testkey = uc( $testkey ); my $sth = $dbh->prepare( "select nick, kword, text from info where upper( kword) = '$testkey'"); $sth->execute; if ( $sth->rows ) { my ( $nick, $infokey, $infotext ) = $sth->fetchrow_array; $nick =~ s/ +$//g; print $sock "PRIVMSG $channel :$infokey -> $infotext ($nick)\n"; } else { print $sock "PRIVMSG $channel :$testkey -> Not Found!\n"; } } # Add an entry to the database #---------------------------------------------------------------- sub add { my $text = shift; my $channel = shift; my $nick = shift; my @row = split( / /, $text ); my $testkey = uc( $row[1] ); my $infoword; my $sth = $dbh->prepare( "select kword from info where upper( kword) = '$testkey'"); $sth->execute; if ( ! $sth->rows ) { $infoword = $row[1]; my $entry = ""; for my $a ( 2..$#row ) { $entry .= "$row[$a] "; } $entry =~ s/\'/\\\'/g; if ( length( $entry ) > 250 ) { print $sock "PRIVMSG $channel :Entry too long... 250 characters max. Try again!\n"; } else { $sth = $dbh->prepare( "insert into info values ( now(), '$nick', '$infoword', '$entry' )" ); $sth->execute; print $sock "PRIVMSG $channel :$infoword -> Added!\n"; } } else { print $sock "PRIVMSG $channel :$infoword -> Already exists, !remove first or !replace!\n"; } } # Replace an entry in the database #---------------------------------------------------------------- sub replace { my $text = shift; my $channel = shift; my $nick = shift; my $unick = uc( $nick ); my $uchan = uc( $channel ); my ( $uth, $ukey, $entry, $authed ); my @row = split( / /, $text ); if ( ! $unlockdb ) { $uth = $dbh->prepare( "select u.newnick from users u, authedchans c where u.buser = c.buser and c.channel = '$uchan' and u.newnick = '$unick'" ); $uth->execute; $authed = $uth->rows; } if ( $authed + $unlockdb ) { $ukey = uc( $row[1] ); $entry = ""; for my $a ( 2..$#row ) { $entry .= "$row[$a] "; } $entry =~ s/\'/\\\'/g; $uth = $dbh->prepare( "update info set dt = now(), text = '$entry', nick = '$nick' where upper( kword ) = '$ukey'" ); $uth->execute; print $sock "PRIVMSG $channel :$row[1] -> Replaced\n"; } else { print $sock "PRIVMSG $channel :Replace function denied to $nick, no priviledge!\n"; } } # Remove an entry from the database #---------------------------------------------------------------- sub remove { my $text = shift; my $channel = shift; my $nick = shift; my @row = split( / /, $text ); $nick = uc( $nick ); $channel = uc( $channel ); my ( $uth, $infoword, $ukey, $authed ); if ( ! $unlockdb ) { $uth = $dbh->prepare( "select u.newnick from users u, authedchans c where u.buser = c.buser and c.channel = '$channel' and u.newnick = '$nick'" ); $uth->execute; $authed = $uth->rows; } if ( $authed + $unlockdb ) { $ukey = uc( $row[1] ); $uth = $dbh->prepare( "delete from info where upper( kword ) = '$ukey'" ); $uth->execute; print $sock "PRIVMSG $channel :$infoword -> Removed\n"; } else { print $sock "PRIVMSG $channel :Delete function denied to $nick, no priviledge!\n"; } } # Return the last time a person joined or parted the channel sub seen { my $nick = shift; $nick =~ s/ +$//g; my $channel = shift; my $wth = $dbh->prepare( "select joined, parted from onchan where channel = '$channel' and nick = '$nick' " ); $wth->execute; if ( ! $wth->rows ) { print $sock "PRIVMSG $channel :$nick is not in the database for $channel\n"; } else { my ( $joined, $parted ) = $wth->fetchrow_array; if ( index( $joined, "2" ) == 0 ) { my ( $ttime, $junk ) = split( /\./, $joined ); print $sock "PRIVMSG $channel :$nick joined $channel at $ttime\n"; } else { my ( $ttime, $junk ) = split( /\./, $parted ); print $sock "PRIVMSG $channel :$nick left $channel at $ttime\n"; } } } # Get the last thing someone said on channel sub lastentry { my $nick = shift; $nick =~ s/ +$//g; my $channel = shift; chomp( $nick, $channel ); my $bth = $dbh->prepare( "select text, dt from chanlog where nick = '$nick' and channel = '$channel' order by dt DESC limit 1 " ); $bth->execute; if ( $bth->rows ) { my ( $btext, $dt ) = $bth->fetchrow_array; my ( $ttime, $junk ) = split( /\./, $dt ); print $sock "PRIVMSG $channel :<$nick> $btext $ttime\n"; } else { print $sock "PRIVMSG $channel :Nothing in database\n"; } } # Get help sub help { my $channel = shift; print $sock "PRIVMSG $channel :Help can be found at http://ozzzy.dhis.org/ozbot.html\n"; } # Get authorization for an admin... returns 1 if true, 0 if false #---------------------------------------------------------------- sub authadmin { my $znick = shift; $znick = uc( $znick ); my $zth = $dbh->prepare( "select buser from users where newnick = '$znick' and admin = 1 " ); $zth->execute; my $znumrows = $zth->rows; return $znumrows; } # Get normal user authorization... returns 1 if true 0 if false #---------------------------------------------------------------- sub authuser { my $znick = shift; my $znick = uc( $znick ); my $zth = $dbh->prepare( "select buser from users where newnick = '$znick'" ); $zth->execute; my $znumrows = $zth->rows; return $znumrows; } # Check password #---------------------------------------------------------------- sub checkpass { my $zuser = shift; my $zpass = shift; my $zth = $dbh->prepare( "select password from users where buser = '$zuser' " ); $zth->execute; my $zrows = $zth->rows; if ( $zrows ) { my ( $zcrypt ) = $zth->fetchrow_array; if ( crypt( $zpass, $zcrypt ) eq $zcrypt ) { return 1; } else { return 0; } } else { return 0; } } # Create password #---------------------------------------------------------------- sub createpass { my $zpass = shift; chomp $zpass; my $zseed = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]; my $zcrypt = crypt( $zpass, $zseed ); return $zcrypt; } # Print output to the log #---------------------------------------------------------------- sub printlog { my $text = shift; open( OUT, ">> $filename" ); print OUT $text; close OUT; } # Cleanup the access lists etc #---------------------------------------------------------------- sub cleanup { my $ath = $dbh->prepare( "delete from authedusers" ); $ath->execute; $ath = $dbh->prepare( "delete from authedchans" ); $ath->execute; $ath = $dbh->prepare( "update users set newnick = NULL" ); $ath->execute; my $now = gmtime( time ); &printlog( "RE-STARTING BOT $now\n$now:Logging onto $server\n" ); } # Connect to the bot, register the nick/botname and join # the required channels #---------------------------------------------------------------- sub startbot { my $now; $sock = IO::Socket::INET->new( PeerAddr => $server, PeerPort => $port, Proto => 'tcp' ) or die "could not make the connection"; while( $line = <$sock> ){ $now = gmtime( time ); print LOG "$now:Starting Bot\n"; if($line =~ /(NOTICE AUTH).*(checking ident)/i){ print $sock "NICK $botnick\nUSER $botuser 0 0 :$bottext\n"; last; } } while( $line = <$sock> ){ print LOG $line; if( $line =~ /^PING/ ){ print $sock "PONG :" . (split(/ :/, $line))[1]; } if( $line =~ /(376|422)/i ){ print $sock "NICKSERV :identify nick_password\n"; last; } } sleep 3; $now = gmtime( time ); chomp( $channels_to_join ); my $bnick = uc( $botnick ); @channels = split( /\,/, $channels_to_join ); foreach my $centry ( @channels ) { print $sock "JOIN $centry\n"; my $wth = $dbh->prepare( "select * from onchan where nick = '$bnick' " ); $wth->execute; if ( ! $wth->rows ) { my $xth = $dbh->prepare( "insert into onchan ( channel, nick, joined, parted ) values ( '$centry', '$bnick', now(), null )" ); $xth->execute; } else { my $xth = $dbh->prepare( "update onchan set joined = now(), parted = null where channel = '$centry' and nick = '$bnick' " ); $xth->execute; } &printlog( "$now:$centry joined\n" ); } } # Create an initial user for the bot #---------------------------------------------------------------- sub initialize{ print "OzBot v1.00 by Rick Saunders\n"; print "----------------------------------------------\n\n"; print "Enter your username: "; my $adminid = ; chomp $adminid; print "Enter your password: "; my $password = ; chomp $password; $adminid = uc( $adminid ); $password = &createpass( $password ); my @channels = split( /\,/, $channels_to_join ); my $sth = $dbh->prepare( "insert into users values ( now(), '$adminid', '$password', null, 1 )" ); $sth->execute; foreach my $centry ( @channels ) { if ( $centry =~ /\#/ ) { $centry = uc( $centry ); $sth = $dbh->prepare( "insert into channels values( '$adminid', '$centry' ) " ); $sth->execute; } } open( DATA, "ozbot.pl" ) or die "Cannot open file!"; my @data = ; close( DATA ); open( OUT, "> ozbot.pl" ); foreach my $line ( @data ) { chomp $line; if ( $line eq "&initialize;" ) { print OUT "#&initialize;\n"; } else { print OUT $line . "\n"; } } close OUT; print "\n\nUser $adminid entered in database for all channels\n"; print "You may now run the script live\n"; exit; }