--- ChivanetConvoBot/convobot 2025/02/03 15:38:12 1.3 +++ ChivanetConvoBot/convobot 2025/02/05 01:03:18 1.6 @@ -9,6 +9,15 @@ # Licensed AGPL-3.0 # # $Log: convobot,v $ +# Revision 1.6 2025/02/05 01:03:18 snw +# Fix invite and seen commands to support screen names with spaces +# +# Revision 1.5 2025/02/03 18:14:15 snw +# Further work on bot +# +# Revision 1.4 2025/02/03 17:31:28 snw +# Further MySQL work +# # Revision 1.3 2025/02/03 15:38:12 snw # Begin SQL work # @@ -38,85 +47,118 @@ my $dbname = ''; my $dbusername = ''; my $dbpw = ''; my $dbconn = ''; +my $autogreet = 'off'; my $chatroom = ''; my $online = 0; my $chat_idle_seconds = 0; my $last_chat_received = time(); my $start_time = time(); +my $dbh = ''; +my $dsn = ''; my @congregants = (); -my %seen = (); - -GetOptions("aimsn=s" => \$botsn, - "aimhost=s" => \$botsrv, - "aimpw=s" => \$botpw, - "idlemax=s" => \$idlemax, - "chatroom=s" => \$chatroom, - "dbhost=s" => \$dbhost, - "dbname=s" => \$dbname, - "dbusername=s" => \$dbusername, - "dbpw=s" => \$dbpw); - or die("error in command line arguments"); - -%signon = ( - screenname => $botsn, - password => $botpw, - host => $botsrv, -); $oscar = Net::OSCAR->new(); -my $dbh = DBI->connect("DBI:mysql:database=$dbname;host=$dbhost", - "$dbusername", "$dbpw", - {'RaiseError' => 1}); sub get_seen_status { + my($sn, $chat) = @_; + + my $sth = $dbh->prepare("SELECT * FROM seen WHERE aim_server=? AND aim_sn=? AND aim_chatroom=? AND sn=?"); + $sth->execute($botsrv, $botsn, $chatroom, $sn); + + if($sth->rows > 0) { + my $hashref = $sth->fetchrow_hashref(); + $chat->chat_send("I last saw $sn on $hashref->{seen_time}."); + } + else { + $chat->chat_send("I have never seen $sn."); + } + +} + +sub update_seen_status { my($sn) = @_; + my $del = $dbh->prepare("DELETE FROM seen WHERE aim_server=? AND aim_sn=? AND aim_chatroom=? AND sn=?"); + $del->execute($botsrv, $botsn, $chatroom, $sn) or die "error DBI->errstr()"; + my $ins = $dbh->prepare("INSERT INTO seen (aim_server, aim_sn, aim_chatroom, sn, seen_time) VALUES (?, ?, ?, ?, ?)"); + my $seentime = localtime(); + $ins->execute($botsrv, $botsn, $chatroom, $sn, $seentime) or die "error DBI->errstr()"; } sub signon_done { print "[OK]\n"; + print "convobot: joining $chatroom..."; $oscar->chat_join($chatroom, 5); + print "[OK]\n"; $online = 1; } +sub oscar_error { + my($oscar, $connection, $error, $description, $fatal) = @_; + + if($fatal != 0) { + die "\nconvobot: fatal OSCAR error: $description\n"; + } + else { + print "\nconvobot: recoverable OSCAR error: $description\n"; + } + +} + sub chat_joined { my($oscar, $chatname, $chat) = @_; + print "bot: chat joined [$chatname]\n"; + $room = $chat; bless $room, "Net::OSCAR::Connection::Chat"; + + print "convobot: connecting to database $dbname\@$dbhost..."; + + $dsn = "DBI:mysql:database=$dbname;host=$dbhost;port=3306;mysql_connect_timeout=5;"; + $dbh = DBI->connect($dsn, $dbusername, $dbpw, {RaiseError => 1}); + die "convobot: failed to connect to MySQL database: DBI->errstr()" unless $dbh; + + print "[OK]\n"; + + $oscar->set_callback_chat_buddy_in(\&chat_buddy_in); + $oscar->set_callback_chat_buddy_out(\&chat_buddy_out); } sub chat_buddy_in { my ($oscar, $who, $chat, $buddy) = @_; - $seen{$who} = localtime(); + update_seen_status($who); if($who ne $botsn) { push(@congregants, $who); - print "[$who] has joined\n"; + print "convobot: [$who] has joined\n"; } else { - print "[$who] has joined (ignoring bot)\n"; + print "convobot: [$who] has joined (ignoring bot)\n"; } - if(time() - $start_time > 2) { - my @phrases = ('Welcome to [room], [user]! :-)', - 'How\'s it going, [user]?', - 'Hey [user]! Bring any snacks?', - 'Heya [user]! Hope your day is going well!', - 'Ooo, [user] has joined [room]! Now the party can start!'); - - my $phrase = $phrases[rand @phrases]; - $phrase =~ s/\[user\]/$who/g; - $phrase =~ s/\[room\]/$chatroom/g; - my $phrasefix = "
$phrase"; - $chat->chat_send($phrasefix); - } - else { - print "Not sending greeting for 2 seconds after startup\n"; + + if($autogreet eq "on") { + if(time() - $start_time > 2) { + my @phrases = ('Welcome to [room], [user]! :-)', + 'How\'s it going, [user]?', + 'Hey [user]! Bring any snacks?', + 'Heya [user]! Hope your day is going well!', + 'Ooo, [user] has joined [room]! Now the party can start!'); + + my $phrase = $phrases[rand @phrases]; + $phrase =~ s/\[user\]/$who/g; + $phrase =~ s/\[room\]/$chatroom/g; + my $phrasefix = "
$phrase"; + $chat->chat_send($phrasefix); + } + else { + print "convobot: not sending greeting for 2 seconds after startup\n"; + } } } @@ -127,7 +169,7 @@ sub chat_buddy_out { $index++ until $congregants[$index] eq $who; splice(@congregants, $index, 1); - print "$who has left\n"; + print "convobot: $who has left\n"; } sub chat_im_in { @@ -137,27 +179,46 @@ sub chat_im_in { my $rawcmd = $hs->parse($message); my @cmd = split(' ', $rawcmd); - $seen{$who} = localtime(); - - if($cmd[0] eq "!seen") { - if(exists($cmd[1])) { - if(exists($seen{$cmd[1]})) { - $chat->chat_send("I last saw $cmd[1] at $seen{$cmd[1]}"); - } - else { - $chat->chat_send("I've never seen $cmd[1]"); - } - } - else { - $chat->chat_send("Syntax: !seen screenname"); - } + update_seen_status($who); + + if($who ne $botsn) { + if($cmd[0] eq "!seen") { + if(exists($cmd[1])) { + my @sna = @cmd[1..$#cmd]; + my $ssn = join(' ', @sna); + get_seen_status($ssn, $chat); + } + else { + $chat->chat_send("Syntax: !seen screenname"); + } + } + elsif($cmd[0] eq "!speak") { + send_idle_message(); + } + elsif($cmd[0] eq "!quote") { + my $fortune = `/usr/games/fortune`; + $room->chat_send($fortune); + } + elsif($cmd[0] eq "!invite") { + if(exists($cmd[1])) { + my @sna = @cmd[1..$#cmd]; + my $ssn = join(' ', @sna); + $chat->invite($ssn, "Please join us in $chatroom!
Requested by $who"); + } + } + elsif($cmd[0] eq "!help") { + $room->chat_send("You can enter the following commands:"); + $room->chat_send(" !seen screenname (find out when screenname was last in the chat)"); + $room->chat_send(" !invite screenname (invite screenname to the chat)"); + $room->chat_send(" !speak (send a random message)"); + $room->chat_send(" !quote (send a quote)"); + } } - - - + + $last_chat_received = time(); - print "chat received from $who; resetting idle counter\n"; + print "convobot: chat received from $who; resetting idle counter\n"; } @@ -182,18 +243,46 @@ sub send_idle_message { $room->chat_send($phrasefix); $last_chat_received = time(); } + } $oscar->set_callback_signon_done(\&signon_done); $oscar->set_callback_chat_joined(\&chat_joined); -$oscar->set_callback_chat_buddy_in(\&chat_buddy_in); -$oscar->set_callback_chat_buddy_out(\&chat_buddy_out); $oscar->set_callback_chat_im_in(\&chat_im_in); +$oscar->set_callback_error(\&oscar_error); print "ChivaNet Conversation Bot v0.0.1\n"; print " Copyright (C) 2025 Coherent Logic Development LLC\n\n"; -print "bot: attempting to sign in... "; +GetOptions("aimsn=s" => \$botsn, + "aimhost=s" => \$botsrv, + "aimpw=s" => \$botpw, + "idlemax=s" => \$idlemax, + "chatroom=s" => \$chatroom, + "dbhost=s" => \$dbhost, + "dbname=s" => \$dbname, + "dbusername=s" => \$dbusername, + "dbpw=s" => \$dbpw, + "autogreet=s" => \$autogreet) + or die("error in command line arguments"); + +%signon = ( + screenname => $botsn, + password => $botpw, + host => $botsrv, +); + +print "AIM Server: $botsrv\n"; +print "AIM Screen Name: $botsn\n"; +print "Chat Room: $chatroom\n"; +print "DB Host: $dbhost\n"; +print "DB Name: $dbname\n"; +print "DB Username: $dbusername\n"; +print "Idle before ping: $idlemax\n"; +print "Auto-Greet: $autogreet\n\n"; + + +print "convobot: attempting to sign in..."; $oscar->signon(%signon); while(1) {