The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package ZenIRCBot;

use 5.010;
use strict;
use warnings;

our $VERSION = '0.01_01';

use AnyEvent::Redis;
use JSON;

=head1 NAME

ZenIRCBot - Perl API for ZenIRCBot

=head1 SYNOPSIS

  use ZenIRCBot;

  my $bot = ZenIRCBot->new();
  $bot->register_commands('ZenIRCBot::Test', [
      { name => 'foo', description => 'Returns bar' }
  ]);

  $bot->subscribe(sub {
      my ($msg, $channel) = @_;
      
      if ($msg->{version} == 1 and $msg->{type} eq 'privmsg') {
          if ($msg->{data}->{message} =~ m/foo/) {
              $bot->send_privmsg(
                  $msg->{data}->{channel},
                  sprintf("%s: bar", $msg->{data}->{sender})
              );
          }
      }
  });

  $bot->run;

=head1 DESCRIPTION

Perl API for ZenIRCBot.

=head1 METHODS

=head2 Standard API Methods

See the L<ZenIRCBot documentation|http://zenircbot.readthedocs.org/en/latest/index.html> 
for more information.

=cut

sub new {
    my $class = shift;
    my $host = shift // 'localhost';
    my $port = shift // 6379;
    my $db = shift // 0;
    
    my $redis = AnyEvent::Redis->new(
        host => $host,
        port => $port,
        on_error => sub { warn @_; }
    );
    # TODO handle db 

    bless({
        redis => $redis,
        services => [],
    }, $class);
}

sub redis { return $_[0]->{redis}; }


=head2 subscribe($func)

Set callback for redis publish events.

=head3 callback($message, $channel)

The callback function is called on Redis subscribe events, with the message
hashref and channel name.

=cut

sub subscribe {
    my ($self, $func) = @_;
    die "Not a CODEREF" if (ref $func ne "CODE");
    $self->get_redis_client->subscribe('in', sub {
        my ($raw, $channel) = @_;
        my $json = JSON->new->allow_nonref;
        my $msg = $json->decode($raw);
        &{$func}($msg, $channel);
    });
}

# Blocking AE loop
sub run {
    my $self = shift;
    AnyEvent->condvar->recv;
}

# ZenIRC functions from std api
sub send_privmsg {
    my ($self, $to, $message) = @_;
    if (ref $to ne 'ARRAY') {
        $to = [$to];
    }

    for my $channel (@{$to}) {
        my $json = JSON->new->allow_nonref->allow_blessed->convert_blessed;
        my $data = $json->encode({
            version => 1,
            type => 'privmsg',
            data => {
                to => $channel,
                message => $message
            }
        });
        
        $self->get_redis_client->publish('out', $data);
    }
}

sub send_admin_message {
    my ($self, $message) = @_;
    $self->redis->get('zenircbot:admin_spew_channels', sub {
        $self->send_privmsg(@_, $message) if (@_);
    });
}

sub register_commands {
    my ($self, $service, $commands) = @_;
    
    return if (!defined($commands));
    $commands = [$commands]
      if (ref($commands) ne "ARRAY");
    
    $self->{cv} = $self->get_redis_client->subscribe('in', sub {
        my $raw = shift;
        
        my $json = JSON->new->allow_nonref;
        my $message = $json->decode($raw);

        if ($message->{version} == 1 and $message->{type} eq 'privmsg') {
            if ($message->{data}->{message} eq 'commands') {
                for my $command (@{$commands}) {
                    $self->send_privmsg(
                        $message->{data}->{sender},
                        sprintf(
                            "%s: %s - %s",
                            $service,
                            $command->{name},
                            $command->{description}
                    ));
                }
            }
        }
    });

    $self->send_admin_message(sprintf("%s online!", $service));
}

sub get_redis_client {
    my ($self) = @_;
    return AnyEvent::Redis->new(
        host => $self->{host},
        port => $self->{port},
        on_error => sub { warn @_; }
    );
}

1;
__END__

=head1 SEE ALSO

L<ZenIRCBot|http://github.com/wraithan/zenircbot>

L<ZenIRCBot Documentation|http://zenircbot.readthedocs.org/en/latest/index.html>

=head1 AUTHOR

Anthony Johnson C<< <aj@ohess.org> >>