--- 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 <strong>$sn</strong> on $hashref->{seen_time}.");
+    }
+    else {
+        $chat->chat_send("I have never seen <strong>$sn</strong>.");
+    }
+    
+}
+
+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 = "<div id=convobot></div>$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 = "<div id=convobot></div>$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 <em>screenname</em>");
-        }
+    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 <em>screenname</em>");
+	    }
+	}
+	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! <br><em>Requested by $who</em>");
+	    }
+	}
+	elsif($cmd[0] eq "!help") {
+	    $room->chat_send("You can enter the following commands:");
+	    $room->chat_send(" <code>!seen <em>screenname</em></code>  (find out when <em>screenname</em> was last in the chat)");
+	    $room->chat_send(" <code>!invite <em>screenname</em></code>  (invite <em>screenname</em> to the chat)");
+	    $room->chat_send(" <code>!speak</code>  (send a random message)");
+	    $room->chat_send(" <code>!quote</code>  (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) {