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