From 9f47b659a4f05cd26d2405eb48ac1f7dd8f0724a Mon Sep 17 00:00:00 2001 From: Thomas Schwery Date: Fri, 25 Nov 2011 10:28:26 +0100 Subject: [PATCH] First draft --- ircbot.pl | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 ircbot.pl diff --git a/ircbot.pl b/ircbot.pl new file mode 100644 index 0000000..af8aab1 --- /dev/null +++ b/ircbot.pl @@ -0,0 +1,213 @@ +#!/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; +}