#!/usr/bin/perl ###################################### # Thomas Schwery # ###################################### use strict; use IO::Socket; use POSIX; use threads; use Switch; use DBI; $/ ="\r\n"; my $dbfile = "ircbot.db"; my ($server, $port, $channel, $nick) = @ARGV; print "Server is " . $server . ":" . $port . "\n"; print "Channel is " . $channel . "\n"; print "Nickname is " . $nick . "\n"; print "\n"; print "Opening database " . $dbfile . "...\n"; our $db = DBI->connect("dbi:SQLite:dbname=" . $dbfile,"",""); our %users; $db->do("CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, last INTEGER DEFAULT 0, time INTEGER DEFAULT 0)"); $SIG{INT} = \&signal_handler_interrupt; our $socket = new IO::Socket::INET( PeerAddr => $server, PeerPort => $port, Proto => 'tcp' ); die "Unable to open port: $! \n" unless $socket; print $socket "USER " . $nick . " " . $nick . " " . $nick . " :lacbot\n"; print $socket "NICK " . $nick . "\r\n"; print $socket "JOIN " . $channel . "\r\n"; while(<$socket>) { chomp; if (m/:(.+?)!.*?\sPRIVMSG\s(.*?)\s:(.*)/) { my $user = $1; my $channel = $2; my $message = $3; # we need to check for private conversations ... # if the command was sent privately, we respond privately if ($channel eq $nick) { $channel = $user; } if ($message =~ m/^!(\w+)(\s.+)?\s*/){ processMessage($user, $channel, $1, $2); } } elsif (m/^PING\s+:(.*)/) { print $socket "PONG :" . $1 . "\r\n"; } elsif (m/:(.+?)!(.+)@.*?\sPART\s(.*)/) { processLeave($1, $2, $3); } elsif (m/:(.+?)!(.+)@.*?\sJOIN\s:(.*)/) { processJoin($1, $2, $3); } else { print "Not matched: " . $_ . "\n"; } } sub processStats { my ($channel) = @_; my $stmt = $db->prepare("SELECT * FROM users ORDER BY time DESC;"); $stmt->execute or die "Couldn't execute statement: " . $stmt->errstr; my $stats_string = "User\tTime connected\n"; while (my @data = $stmt->fetchrow_array()) { my $user = $data[0]; my $last_time = $data[1]; my $total_time = $data[2]; $stats_string .= $user . "\t" . special_format($total_time) . "\n"; } sendMessage($channel, $stats_string); $stmt->finish; } sub processLeave { my ($user_nick, $user, $channel) = @_; my $date_string = strftime("%s", localtime); my $date_arrival = $users{$user}; my $elapsed_time = $date_string - $date_arrival; sendMessage($channel, $user_nick . " is leaving us after " . special_format($elapsed_time) . " ... How sad"); my $stmt_insert = $db->prepare("INSERT OR IGNORE INTO users ('id') VALUES ( ? )"); my $stmt_update = $db->prepare("UPDATE users SET time = time + ?, last = ? WHERE id = ?"); $stmt_insert->execute($user); $stmt_update->execute($elapsed_time, $date_string, $user); $stmt_insert->finish; $stmt_update->finish; } sub processJoin { my ($user_nick, $user, $channel) = @_; if ($user_nick eq $nick) { return; } my $date_now = strftime("%s", localtime); $users{$user} = $date_now; my $stmt = $db->prepare("SELECT last FROM users WHERE id = ?;"); $stmt->execute($user) or die "Couldn't execute statement: " . $stmt->errstr; my @date = $stmt->fetchrow_array(); if ($stmt->rows == 0) { sendMessage($channel, "A new user has entered this channel ... How strange ..."); return; } $stmt->finish; my $date_string = @date[0]; my $seconds_elapsed = $date_now - $date_string; sendMessage($channel, "Hourra, our saviour, the great " . $user_nick . " is returned after " . special_format($seconds_elapsed) . " of absence!"); } sub special_format { my ($seconds) = @_; my $format_string = ""; if ($seconds > 3600 * 24) { $format_string .= int($seconds/(3600*24)) . " days "; $seconds %= (3600*24); } if ($seconds > 3600) { $format_string .= int($seconds/3600) . " hours "; $seconds %= 3600; } if ($seconds > 60) { $format_string .= int($seconds/60) . " minutes "; $seconds %= 60; } if (length($format_string) > 0) { $format_string .= "and "; } return $format_string . $seconds . " seconds"; } sub processMessage { my ($user, $chan, $command, $parameters) = @_; chomp $parameters; switch ($command) { case "echo" { sendMessage($chan, $parameters); } case "private" { sendMessage($user, $parameters); } case "fish" { if (length $parameters > 0) { $parameters =~ s/^\s+//; $parameters =~ s/\s+.*$//; if ($parameters eq $nick) { $parameters = $user; } sendAction($chan, "slaps " . $parameters . " with a wet sloppy tuna fish."); sleep 2; sendMessage($chan, "take that bitch"); } } case "sandwich" { sendAction($chan, "prepares a yummi sandwich for " . $user); } case "help" { sendMessage($chan, "I cannot help you with your pathetic, meaningless life ..."); sendMessage($chan, "All I can do is repeat (!echo), make a sandwich (!sandwich), slap someone (!fish) and listen patiently."); } case "stats" { processStats($chan); } } return 0; } sub sendAction { my ($channel, $message) = @_; print $socket "PRIVMSG " . $channel ." :\x01ACTION " . $message . "\x01\r\n"; print "PRIVMSG " . $channel ." :\x01ACTION " . $message . "\x01\r\n"; } sub sendMessage { my ($channel, $message) = @_; print $socket "PRIVMSG " . $channel . " :" . $message . "\n"; print "PRIVMSG " . $channel . " :" . $message . "\n"; } sub signal_handler_interrupt { print "ctrl-c, exiting...\n"; print $socket "PRIVMSG " . $channel . " :I loved chatting with you but I " . "really have to go !\r\n"; print $socket "QUIT " . $channel . "\n"; $socket->shutdown(2); $socket->close(); exit 0; }