This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
irc-perlbot/ircbot.pl
Thomas Schwery 9f47b659a4 First draft
2011-11-25 10:28:26 +01:00

213 lines
6.3 KiB
Perl

#!/usr/bin/perl
######################################
# Thomas Schwery <thomas@schwery.me> #
######################################
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;
}