# $Id$ # TODO - Consider auto reconnection in case the bot cycles. package Util::Backend; use warnings; use strict; use IO::Socket::INET; use Carp qw(croak); use Util::Message; sub DEBUG () { 0 } use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw( svc_init svc_shutdown msg_read msg_send svc_run svc_stop msg_oneshot ); use Storable qw(nfreeze thaw); my ($buffer, $read_length); my $socket; my $caller_package; sub svc_init { my ($host, $port) = @_; $socket = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port, ); croak "Couldn't connect to $host : $port - $!" unless defined $socket; binmode($socket); $socket->autoflush(); $buffer = ""; $read_length = undef; } sub svc_shutdown { close $socket; $socket = undef; } # Algorithm, and some code, stolen from POE::Filter::Reference. Used # here in ways that make it faster. Used with express permission of # the author. :) sub msg_read { while (1) { if (defined $read_length) { if (length($buffer) >= $read_length) { my $message = thaw(substr($buffer, 0, $read_length, "")); $read_length = undef; $message->{ts30clirecv} = time(); return $message; } } elsif ($buffer =~ s/^(\d+)\0//) { $read_length = $1; next; } my $octets_read = sysread($socket, $buffer, 4096, length($buffer)); return unless $octets_read; } } # Return a message to the parent process. Simply format it and send # it off. sub msg_send { my $message = shift; $message->{ts40clisend} = time(); my $streamable = nfreeze $message; print $socket length($streamable), chr(0), $streamable; } # A do-everything loop for sinking events. my $service_running; sub svc_run { $caller_package = caller(); svc_init(@_); $service_running = 1; while ($service_running and my $msg = msg_read()) { my $function = $msg->event(); warn "no function in message" unless defined $function; $function = "on_" . $function; $function = "on_unhandled" unless $caller_package->can($function); if ($caller_package->can($function)) { no strict 'refs'; "$caller_package\::$function"->($msg); next; } DEBUG and warn "unhandled irc event: ", $msg->event(); } svc_shutdown(); } # Stop the do-everything loop. sub svc_stop { $service_running = 0; } # One-shot message sending, good for agents. sub msg_oneshot { my ($host, $port, $network, $channel, $event, $message) = @_; svc_init($host, $port); msg_send( Util::Message->new( { channel => $channel, event => $event, response => $message, network => $network, bot => "irc_client_$network", } ) ); svc_shutdown(); } 1;