File:  [Coherent Logic Development] / ChivanetConvoBot / convobot
Revision 1.6: download - view: text, annotated - select for diffs
Wed Feb 5 01:03:18 2025 UTC (5 months, 3 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Fix invite and seen commands to support screen names with spaces

    1: #!/usr/bin/env perl
    2: 
    3: # 
    4: # ChivaNet Conversation Bot 
    5: #  Copyright (C) 2025 Coherent Logic Development LLC
    6: #
    7: # Author: Serena Willis <snw@coherent-logic.com>
    8: #
    9: # Licensed AGPL-3.0
   10: #
   11: # $Log: convobot,v $
   12: # Revision 1.6  2025/02/05 01:03:18  snw
   13: # Fix invite and seen commands to support screen names with spaces
   14: #
   15: # Revision 1.5  2025/02/03 18:14:15  snw
   16: # Further work on bot
   17: #
   18: # Revision 1.4  2025/02/03 17:31:28  snw
   19: # Further MySQL work
   20: #
   21: # Revision 1.3  2025/02/03 15:38:12  snw
   22: # Begin SQL work
   23: #
   24: # Revision 1.2  2025/02/03 04:28:34  snw
   25: # Fix syntax message
   26: #
   27: # Revision 1.1.1.1  2025/02/03 04:22:49  snw
   28: # Initial Commit
   29: #
   30: #
   31: #
   32: 
   33: use Net::OSCAR;
   34: use Getopt::Long;
   35: use Data::Dumper;
   36: use HTML::Strip;
   37: use DBI;
   38: 
   39: my $idlemax = 1800;
   40: my $botsn = '';
   41: my $botsrv = '';
   42: my $botpw = '';
   43: my $rasurl = '';
   44: 
   45: my $dbhost = '';
   46: my $dbname = '';
   47: my $dbusername = '';
   48: my $dbpw = '';
   49: my $dbconn = '';
   50: my $autogreet = 'off';
   51: 
   52: my $chatroom = '';
   53: my $online = 0;
   54: my $chat_idle_seconds = 0;
   55: my $last_chat_received = time();
   56: my $start_time = time();
   57: my $dbh = '';
   58: my $dsn = '';
   59: 
   60: my @congregants = ();
   61: 
   62: $oscar = Net::OSCAR->new();
   63: 
   64: sub get_seen_status {
   65:     my($sn, $chat) = @_;
   66: 
   67:     my $sth = $dbh->prepare("SELECT * FROM seen WHERE aim_server=? AND aim_sn=? AND aim_chatroom=? AND sn=?");
   68:     $sth->execute($botsrv, $botsn, $chatroom, $sn);
   69: 
   70:     if($sth->rows > 0) {
   71:         my $hashref = $sth->fetchrow_hashref();
   72:         $chat->chat_send("I last saw <strong>$sn</strong> on $hashref->{seen_time}.");
   73:     }
   74:     else {
   75:         $chat->chat_send("I have never seen <strong>$sn</strong>.");
   76:     }
   77:     
   78: }
   79: 
   80: sub update_seen_status {
   81:     my($sn) = @_;
   82: 
   83:     my $del = $dbh->prepare("DELETE FROM seen WHERE aim_server=? AND aim_sn=? AND aim_chatroom=? AND sn=?");
   84:     $del->execute($botsrv, $botsn, $chatroom, $sn) or die "error DBI->errstr()";
   85:     
   86:     my $ins = $dbh->prepare("INSERT INTO seen (aim_server, aim_sn, aim_chatroom, sn, seen_time) VALUES (?, ?, ?, ?, ?)");
   87:     my $seentime = localtime();
   88:     $ins->execute($botsrv, $botsn, $chatroom, $sn, $seentime) or die "error DBI->errstr()";        
   89: }
   90: 
   91: sub signon_done {
   92:     print "[OK]\n";
   93:     print "convobot:  joining $chatroom...";
   94:     $oscar->chat_join($chatroom, 5);   
   95:     print "[OK]\n";
   96:     $online = 1;
   97: }
   98: 
   99: sub oscar_error {
  100:     my($oscar, $connection, $error, $description, $fatal) = @_;
  101: 
  102:     if($fatal != 0) {
  103:         die "\nconvobot:  fatal OSCAR error:  $description\n";        
  104:     }
  105:     else {
  106:         print "\nconvobot:  recoverable OSCAR error: $description\n";
  107:     }
  108:    
  109: }
  110: 
  111: sub chat_joined {
  112:     my($oscar, $chatname, $chat) = @_;
  113: 
  114:     print "bot:  chat joined [$chatname]\n";
  115:     
  116:     $room = $chat;
  117:     bless $room, "Net::OSCAR::Connection::Chat";
  118: 
  119:     print "convobot:  connecting to database $dbname\@$dbhost...";
  120: 
  121:     $dsn = "DBI:mysql:database=$dbname;host=$dbhost;port=3306;mysql_connect_timeout=5;";
  122:     $dbh = DBI->connect($dsn, $dbusername, $dbpw, {RaiseError => 1});
  123:     die "convobot:  failed to connect to MySQL database: DBI->errstr()" unless $dbh;
  124: 
  125:     print "[OK]\n";
  126: 
  127:     $oscar->set_callback_chat_buddy_in(\&chat_buddy_in);
  128:     $oscar->set_callback_chat_buddy_out(\&chat_buddy_out);
  129: }
  130: 
  131: sub chat_buddy_in {
  132:     my ($oscar, $who, $chat, $buddy) = @_;
  133: 
  134:     update_seen_status($who);
  135:     
  136:     if($who ne $botsn) {
  137:         push(@congregants, $who);
  138:         print "convobot:  [$who] has joined\n";        
  139:     }
  140:     else {
  141:         print "convobot:  [$who] has joined (ignoring bot)\n";
  142:     }
  143: 
  144: 
  145:     if($autogreet eq "on") {
  146:         if(time() - $start_time > 2) {
  147:             my @phrases = ('Welcome to [room], [user]! :-)',
  148:                            'How\'s it going, [user]?',
  149:                            'Hey [user]! Bring any snacks?',
  150:                            'Heya [user]! Hope your day is going well!',
  151:                            'Ooo, [user] has joined [room]! Now the party can start!');
  152:             
  153:             my $phrase = $phrases[rand @phrases];
  154:             $phrase =~ s/\[user\]/$who/g;
  155:             $phrase =~ s/\[room\]/$chatroom/g;       
  156:             my $phrasefix = "<div id=convobot></div>$phrase";
  157:             $chat->chat_send($phrasefix);
  158:         }
  159:         else {
  160:             print "convobot:  not sending greeting for 2 seconds after startup\n";
  161:         }
  162:     }
  163: }
  164: 
  165: sub chat_buddy_out {
  166:     my ($oscar, $who, $chat) = @_;
  167:     my $index = 0;
  168: 
  169:     $index++ until $congregants[$index] eq $who;
  170:     splice(@congregants, $index, 1);
  171: 
  172:     print "convobot:  $who has left\n";
  173: }
  174: 
  175: sub chat_im_in {
  176:     my($oscar, $who, $chat, $message) = @_;
  177: 
  178:     my $hs = HTML::Strip->new();
  179:     my $rawcmd = $hs->parse($message);
  180:     my @cmd = split(' ', $rawcmd);
  181: 
  182:     update_seen_status($who);
  183: 
  184:     if($who ne $botsn) {
  185: 	if($cmd[0] eq "!seen") {
  186: 	    if(exists($cmd[1])) {
  187: 		my @sna = @cmd[1..$#cmd];
  188: 		my $ssn = join(' ', @sna);
  189: 		get_seen_status($ssn, $chat);
  190: 	    }
  191: 	    else {
  192: 		$chat->chat_send("Syntax: !seen <em>screenname</em>");
  193: 	    }
  194: 	}
  195: 	elsif($cmd[0] eq "!speak") {
  196: 	    send_idle_message();
  197: 	}
  198: 	elsif($cmd[0] eq "!quote") {
  199: 	    my $fortune = `/usr/games/fortune`;
  200: 	    $room->chat_send($fortune);
  201: 	}
  202: 	elsif($cmd[0] eq "!invite") {
  203: 	    if(exists($cmd[1])) {
  204: 		my @sna = @cmd[1..$#cmd];
  205: 		my $ssn = join(' ', @sna);		
  206: 		$chat->invite($ssn, "Please join us in $chatroom! <br><em>Requested by $who</em>");
  207: 	    }
  208: 	}
  209: 	elsif($cmd[0] eq "!help") {
  210: 	    $room->chat_send("You can enter the following commands:");
  211: 	    $room->chat_send(" <code>!seen <em>screenname</em></code>  (find out when <em>screenname</em> was last in the chat)");
  212: 	    $room->chat_send(" <code>!invite <em>screenname</em></code>  (invite <em>screenname</em> to the chat)");
  213: 	    $room->chat_send(" <code>!speak</code>  (send a random message)");
  214: 	    $room->chat_send(" <code>!quote</code>  (send a quote)");	    
  215: 	}
  216:     }
  217: 	
  218:                 
  219:     $last_chat_received = time();    
  220: 
  221:     print "convobot:  chat received from $who; resetting idle counter\n";
  222:     
  223: }
  224: 
  225: sub send_idle_message {
  226: 
  227:     my @phrases = ('Hey [user]! How are you today?',
  228:                    'I think [user] should bring us some pizza!',
  229:                    'What\'s everyone up to here?',
  230:                    'My, what a beautiful day for a chat here in [room]!',
  231:                    '[user] always has the most interesting things to say.',
  232:                    'Remember that time [user] was talking here in [room]?',
  233:                    'What do all you [room] chatters think about pie?',
  234:                    '[room] seems dead :\'(. That makes me sad! Maybe [user] has something interesting to say?');
  235: 
  236:     my $congregant = $congregants[rand @congregants];
  237:     my $phrase = $phrases[rand @phrases];
  238:     $phrase =~ s/\[user\]/$congregant/g;
  239:     $phrase =~ s/\[room\]/$chatroom/g;    
  240:     my $phrasefix = "<div id=convobot></div>$phrase";
  241:     
  242:     if(ref($room) eq "Net::OSCAR::Connection::Chat") {
  243:         $room->chat_send($phrasefix);
  244:         $last_chat_received = time();
  245:     }
  246:     
  247: }
  248: 
  249: $oscar->set_callback_signon_done(\&signon_done);
  250: $oscar->set_callback_chat_joined(\&chat_joined);
  251: $oscar->set_callback_chat_im_in(\&chat_im_in);
  252: $oscar->set_callback_error(\&oscar_error);
  253: 
  254: print "ChivaNet Conversation Bot v0.0.1\n";
  255: print " Copyright (C) 2025 Coherent Logic Development LLC\n\n";
  256: 
  257: GetOptions("aimsn=s" => \$botsn,
  258:            "aimhost=s" => \$botsrv,
  259:            "aimpw=s" => \$botpw,
  260:            "idlemax=s" => \$idlemax,
  261:            "chatroom=s" => \$chatroom,
  262:            "dbhost=s" => \$dbhost,
  263:            "dbname=s" => \$dbname,
  264:            "dbusername=s" => \$dbusername,
  265:            "dbpw=s" => \$dbpw,
  266:            "autogreet=s" => \$autogreet)
  267:     or die("error in command line arguments");
  268: 
  269: %signon = (
  270:     screenname => $botsn,
  271:     password => $botpw,
  272:     host => $botsrv,
  273: ); 
  274: 
  275: print "AIM Server:        $botsrv\n";
  276: print "AIM Screen Name:   $botsn\n";
  277: print "Chat Room:         $chatroom\n";
  278: print "DB Host:           $dbhost\n";
  279: print "DB Name:           $dbname\n";
  280: print "DB Username:       $dbusername\n";
  281: print "Idle before ping:  $idlemax\n";
  282: print "Auto-Greet:        $autogreet\n\n";
  283: 
  284: 
  285: print "convobot:  attempting to sign in...";
  286: $oscar->signon(%signon);
  287: 
  288: while(1) {
  289:     $oscar->do_one_loop();
  290:     $chat_idle_seconds = time() - $last_chat_received;
  291:     
  292:     if($chat_idle_seconds > $idlemax) {
  293:         send_idle_message();
  294:     }
  295: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>