The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Bot::Cobalt::Plugin::Extras::Money;
our $VERSION = '0.016000';

use 5.10.1;

use Bot::Cobalt;
use Bot::Cobalt::Common;

use URI::Escape;
use HTTP::Request;

sub new { bless {}, shift }

sub Cobalt_register {
  my ($self, $core) = splice @_, 0, 2;

  $self->{Cached} = {};
  
  register( $self, 'SERVER',
    qw/
      public_cmd_currency
      public_cmd_cc
      public_cmd_money
      
      currencyconv_rate_recv
      currencyconv_expire_cache
    /
  );

  logger->info("Loaded: cc money currency");

  $core->timer_set( 1200,
    {
      Event => 'currencyconv_expire_cache',
      Alias => $core->get_plugin_alias($self),
    },
    'CURRENCYCONV_CACHE'
  );

  return PLUGIN_EAT_NONE 
}

sub Cobalt_unregister {
  my ($self, $core) = splice @_, 0, 2;
  
  logger->info("Unloaded");
  
  return PLUGIN_EAT_NONE
}

sub Bot_currencyconv_expire_cache {
  my ($self, $core) = splice @_, 0, 2;
  
  for my $fromto (keys %{ $self->{Cached} }) {
    my $delta = time - $self->{Cached}->{$fromto}->{TS};

    if ($delta >= 1200) {
      logger->debug("expired cached: $fromto");

      delete $self->{Cached}->{$fromto};
    }
  }
  
  $core->timer_set( 1200,
    {
      Event => 'currencyconv_expire_cache',
      Alias => $core->get_plugin_alias($self),
    },
    'CURRENCYCONV_CACHE'
  );
  
  return PLUGIN_EAT_ALL;
}

sub Bot_public_cmd_currency {
  my ($self, $core) = splice @_, 0, 2;
  my $msg     = ${ $_[0] };
  my $context = $msg->context;
  
  my $channel = $msg->channel;

  my $message = $msg->message_array;
  my ($value, $from, undef, $to) = @$message;
  
  unless ($value && $from && $to) {
    broadcast( 'message', $context, $channel,
      "Syntax: !cc <value> <abbrev> TO <abbrev>"
    );
    return PLUGIN_EAT_ALL
  }
  
  my $valid_val    = qr/^(\d+)?\.?(\d+)?$/;
  my $valid_abbrev = qr/^[a-zA-Z]{3}$/;

  unless ($value =~ $valid_val) {
    broadcast( 'message', $context, $channel,
      "$value is not a valid quantity."
    );  
    return PLUGIN_EAT_ALL
  }
  
  unless ($from =~ $valid_abbrev && $to =~ $valid_abbrev) {
    broadcast( 'message', $context, $channel,
      "Currency codes must be three-letter abbreviations."
    );
    return PLUGIN_EAT_ALL
  }

  $self->_request_conversion_rate(
    uc($from), uc($to), $value, $context, $channel
  );
  
  return PLUGIN_EAT_ALL
}

sub Bot_public_cmd_cc    { Bot_public_cmd_currency(@_) }
sub Bot_public_cmd_money { Bot_public_cmd_currency(@_) }

sub Bot_currencyconv_rate_recv {
  my ($self, $core) = splice @_, 0, 2;
  my $response = ${ $_[1] };
  my $args     = ${ $_[2] };
  my ($value, $context, $channel, $from, $to) = @$args;
  
  unless ($response->is_success) {
    if ($response->code == 500) {
      broadcast( 'message', $context, $channel,
        "Received error 500; is your currency code valid?"
      );
    } else {
      broadcast( 'message', $context, $channel,
        "HTTP failed: ".$response->code
      );
    }
    return PLUGIN_EAT_ALL
  }

  my $content = $response->decoded_content;
  
  my($rate,$converted);
  if ( $content =~ /<double.*>(.*)<\/double>/i ) {
    $rate = $1||1;
    $converted = sprintf("%.2f", $value * $rate);
  } else {
    broadcast( 'message', $context, $channel,
      "Failed to retrieve currency conversion ($from -> $to)"
    );
    return PLUGIN_EAT_ALL
  }

  my $cachekey = "${from}-${to}";
  $self->{Cached}->{$cachekey} = {
    Rate => $rate,
    TS   => time,
  };
  
  broadcast( 'message', $context, $channel,
    "$value $from == $converted $to"
  );
  
  return PLUGIN_EAT_ALL
}

sub _request_conversion_rate {
  my ($self, $from, $to, $value, $context, $channel) = @_;
  return unless $from and $to;

  ## maybe cached
  my $cachekey = "${from}-${to}";
  if ($self->{Cached}->{$cachekey}) {
    my $cachedrate = $self->{Cached}->{$cachekey}->{Rate};
    my $converted = $value * $cachedrate;

    broadcast( 'message', $context, $channel,
      "$value $from == $converted $to"
    );

    return 1
  }

  my $uri = 
     "http://www.webservicex.net/CurrencyConvertor.asmx"
    ."/ConversionRate?FromCurrency=${from}&ToCurrency=${to}";
  
  if ( core()->Provided->{www_request} ) {
    my $req = HTTP::Request->new( 'GET', $uri ) || return undef;

    broadcast( 'www_request',
      $req,
      'currencyconv_rate_recv',
      [ $value, $context, $channel, $from, $to ],
    );
  } else {
    broadcast( 'message', $context, $channel,
      "No async HTTP available; try loading Bot::Cobalt::Plugin::WWW"
    );
  }
}

1;
__END__

=pod

=head1 NAME

Bot::Cobalt::Plugin::Extras::Money - Currency conversion plugin

=head1 USAGE

  !cc 20 NZD to USD
  <cobalt2> 20 NZD == 16.564 USD

=head1 DESCRIPTION

Uses L<http://www.webservicex.net> to handle currency conversion.

Requires L<Bot::Cobalt::Plugin::WWW>

=head1 AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

L<http://www.cobaltirc.org>

=cut