X-Git-Url: http://git.annexia.org/?p=xavierbot.git;a=blobdiff_plain;f=xavierbot.pl;h=bfdd9bfb8b01806d72f13684b9f1c5c2a32f5bad;hp=ce997a080a6fe8231538ea8ae9a8ad3caf7b2400;hb=1ddb7873d30b80c638d22ec43a771bdb3fc5cb4c;hpb=46fc785c09ddc418c05dfc30e2a20e015dde75e8;ds=sidebyside diff --git a/xavierbot.pl b/xavierbot.pl index ce997a0..bfdd9bf 100755 --- a/xavierbot.pl +++ b/xavierbot.pl @@ -1,73 +1,103 @@ #!/usr/bin/perl -wT +# xavierbot : an OCaml interpreter IRC bot. +# By Richard W.M. Jones . +# This code is in the Public Domain. +# $Id: xavierbot.pl,v 1.7 2007/06/29 07:55:47 rjones Exp $ use strict; - -use POE qw(Component::IRC); +use POE qw(Component::IRC Wheel::Run); #---------------------------------------------------------------------- # Start of configuration. my $nick = "xavierbot"; my $ircname = "Xavierbot"; # Printable name. -my $server = "devserv.devel.redhat.com"; +my $server = "chat.freenode.net"; +#my $server = "devserv.devel.redhat.com"; my $port = 6667; - -my @channels = ("#ocaml"); +my $channel = "#ocaml"; # End of configuration. #---------------------------------------------------------------------- -my $irc = POE::Component::IRC->spawn ( - nick => $nick, - ircname => $ircname, - server => $server, - port => $port, - ) or die "POE::Component::IRC->spawn failed: $!"; +$ENV{PATH} = "/usr/bin:/bin"; POE::Session->create ( package_states => [ - main => [ qw(_default _start irc_001 irc_public) ], + main => [ qw(_default _start irc_001 irc_public got_stdout got_sigchld) ], ], - heap => { irc => $irc }, ); -$poe_kernel->run (); +POE::Kernel->run (); exit 0; -sub _start { +sub _start +{ my ($kernel, $heap) = @_[KERNEL,HEAP]; + my $irc = POE::Component::IRC->spawn + ( + nick => $nick, + ircname => $ircname, + server => $server, + port => $port, + ) or die "POE::Component::IRC->spawn failed: $!"; + + my $ocaml = start_toplevel (); + + $kernel->sig(CHLD => qw(got_sigchld)); + + $heap->{irc} = $irc; + $heap->{ocaml} = $ocaml; + my $irc_session = $heap->{irc}->session_id (); $kernel->post ($irc_session => register => "all"); $kernel->post ($irc_session => connect => { }); + undef; } -sub irc_001 { +sub irc_001 +{ my ($kernel, $sender) = @_[KERNEL,SENDER]; my $poco_object = $sender->get_heap (); print "Connected to ", $poco_object->server_name (), "\n"; - $kernel->post ($sender => join => $_ ) for @channels; + $kernel->post ($sender => join => $channel); undef; } -sub irc_public { - my ($kernel, $sender, $who, $where, $what) = - @_[KERNEL,SENDER,ARG0,ARG1,ARG2]; +sub irc_public +{ + my ($kernel, $sender, $who, $where, $what, $heap) = + @_[KERNEL,SENDER,ARG0,ARG1,ARG2,HEAP]; my $nick = (split /!/, $who)[0]; my $channel = $where->[0]; print "got: $what\n"; - if (my ($rot13) = $what =~ /^rot13 (.+)/) { - $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M]; - $kernel->post ($sender => privmsg => $channel => "$nick: $rot13"); + if (my ($stmt) = $what =~ /^\s*([^#].*;;)\s*$/) { + $heap->{ocaml}->put ("$stmt\n"); + } + # XXX How to interpolate $nick into the patterns? + elsif ($what =~ /^\s*xavierbot\b.*\bhelp\b/) { + my $nick = (split /!/, $who)[0]; + $kernel->post ($sender => privmsg => $channel => + "$nick: expr ;; evaluate expr in OCaml toplevel"); + $kernel->post ($sender => privmsg => $channel => + "$nick: help help message"); + $kernel->post ($sender => privmsg => $channel => + "$nick: restart restart the toplevel"); + } + elsif ($what =~ /^\s*xavierbot\b.*\brestart\b/) { + print STDOUT "got instruction to restart ...\n"; + restart_toplevel ($heap->{ocaml}); } undef; } -sub _default { +sub _default +{ my ($event, $args) = @_[ARG0 .. $#_]; my @output = ("$event: "); @@ -81,3 +111,49 @@ sub _default { print STDOUT join " ", @output, "\n"; return 0; } + +#---------------------------------------------------------------------- + +# Toplevel wrote something. + +sub got_stdout +{ + my ($kernel,$heap, $input, $wheel_id) = @_[KERNEL,HEAP,ARG0,ARG1]; + print "Child said: $input\n"; + $kernel->post ($heap->{irc} => privmsg => $channel => "$input"); +} + +# Got a SIGCHLD, so start the bot up again. + +sub got_sigchld +{ + my ($kernel, $heap) = @_[KERNEL,HEAP]; + my $ocaml = start_toplevel (); + $heap->{ocaml} = $ocaml; +} + +# Start up the toplevel (assumes it's not running). + +sub start_toplevel +{ + return POE::Wheel::Run->new + ( + Program => "./ocamlbotwrapper", + StdoutEvent => "got_stdout", + StderrEvent => "got_stdout", + ) or die "POE::Wheel::Run->new ./ocamlbotwrapper failed: $!"; +} + +# Restart the toplevel - kill the old one, and a new one +# will be spawned after we get the SIGCHLD signal. +# +# XXX Can't send signal to setuid child, so instead just close +# stdin. + +sub restart_toplevel +{ + my $ocaml = shift; + + $ocaml->kill (9); + $ocaml->shutdown_stdin; +}