The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Bot::Cobalt::IRC::Role::AdminCmds;
$Bot::Cobalt::IRC::Role::AdminCmds::VERSION = '0.021003';
use strictures 2;
use Scalar::Util 'reftype';

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

use Try::Tiny;

use Moo::Role;


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

  return PLUGIN_EAT_ALL unless
    $core->auth->has_flag($context, $src_nick, 'SUPERUSER');
  
  my $cmd = lc($msg->message_array->[0] || 'list');
  my $meth = '_cmd_'.$cmd;
  unless ( $self->can($meth) ) {
    broadcast message => $msg->context, $msg->channel,
      "Unknown command; try one of: list, current, connect, disconnect";
    return PLUGIN_EAT_ALL
  }
  
  logger->debug("Dispatching $cmd for $src_nick");
  $self->$meth($msg)
}

sub _cmd_list {
  my ($self, $msg) = @_;
  my @contexts = keys %{ core->Servers };
  my $pcfg = plugin_cfg($self);
  ## FIXME look for contexts that are conf'd but not active
  
  broadcast message => $msg->context, $msg->channel,
    "Active contexts: " . join ' ', @contexts ;
  
  PLUGIN_EAT_ALL
}

sub _cmd_current {
  my ($self, $msg) = @_;

  broadcast message => $msg->context, $msg->channel,
    "Currently on context ".$msg->context;

  PLUGIN_EAT_ALL
}

{ no warnings 'once'; *_cmd_reconnect = *_cmd_connect; }
sub _cmd_connect {
  my ($self, $msg) = @_;
  
  my $pcfg = plugin_cfg($self);
  
  unless (ref $pcfg && reftype $pcfg eq 'HASH' && keys %$pcfg) {
    broadcast message => $msg->context, $msg->channel,
      "Could not locate any network configuration.";
    logger->error("_cmd_connect could not find an IRC network cfg");
    return PLUGIN_EAT_ALL
  }
  
  my $target_ctxt = $msg->message_array->[1];
  unless (defined $target_ctxt) {
    broadcast message => $msg->context, $msg->channel,
      "No context specified.";
    return PLUGIN_EAT_ALL
  }
  
  unless ($pcfg->{Networks}->{$target_ctxt}) {
    broadcast message => $msg->context, $msg->channel,
      "Could not locate configuration for context $target_ctxt";
    return PLUGIN_EAT_ALL
  }
  
  ## Do we already have this context?
  if (my $ctxt_obj = irc_context($target_ctxt) ) {
    if ($ctxt_obj->connected) {
      broadcast message => $msg->context, $msg->channel,
        "Attempting reconnect for context $target_ctxt";
    }
    logger->info("Attempting reconnect for context $target_ctxt");
    broadcast ircplug_disconnect => $target_ctxt;
    broadcast ircplug_connect => $target_ctxt;
    broadcast ircplug_timer_serv_retry =>
      +{ context => $target_ctxt, delay => 300 } ;
    return PLUGIN_EAT_ALL
  }

  broadcast message => $msg->context, $msg->channel,
    "Issuing connect for context $target_ctxt";
  
  my $src_nick = $msg->src_nick;
  my $auth_usr = core->auth->username($msg->context, $src_nick);
  
  logger->info(
   "Issuing connect for context $target_ctxt",
   "(Issued by $src_nick [$auth_usr])"
  );
    
  broadcast ircplug_connect => $target_ctxt;
  
  return PLUGIN_EAT_ALL 
}

sub _cmd_disconnect {
  my ($self, $msg) = @_;
  my $target_ctxt = $msg->message_array->[1];
  
  unless (defined $target_ctxt) {
    broadcast message => $msg->context, $msg->channel,
      "No context specified.";
    return PLUGIN_EAT_ALL
  }
  
  my $ctxt_obj;
  unless ($ctxt_obj = irc_context($target_ctxt) ) {
    broadcast message => $msg->context, $msg->channel,
      "Could not find context object for $target_ctxt";
    return PLUGIN_EAT_ALL
  }

  unless (keys %{ core->Servers } > 1) {
    broadcast message => $msg->context, $msg->channel,
      "Refusing disconnect; have no other active contexts.";
    return PLUGIN_EAT_ALL
  }
  
  broadcast message => $msg->context, $msg->channel,
    "Attempting to disconnect from $target_ctxt";

  my $src_nick = $msg->src_nick;
  my $auth_usr = core->auth->username($msg->context, $src_nick);
  
  logger->info(
   "Issuing disconnect for context $target_ctxt",
   "(Issued by $src_nick [$auth_usr])"
  );

  broadcast ircplug_disconnect => $target_ctxt;
  
  return PLUGIN_EAT_ALL
}


sub Bot_ircplug_timer_serv_retry {
  my ($self, $core) = splice @_, 0, 2;
  my $hints = ${ $_[0] };
    
  my $context = $hints->{context};
  my $delay   = $hints->{delay} || 300;

  logger->debug("ircplug_timer_serv_retry called for $context");
  my $ctxt_obj;
  unless ($ctxt_obj = irc_context($context) && $ctxt_obj->connected) {
    logger->info("Attempting reconnect to $context . . .");
    broadcast ircplug_connect => $context;
    core->timer_set( $delay,
      +{
        Event => 'ircplug_timer_serv_retry',
        Args  => [ +{ context => $context, delay => $delay } ],
      },
    );
  }
  
  return PLUGIN_EAT_ALL
}

1;
__END__

=pod

=head1 NAME

Bot::Cobalt::IRC::Role::AdminCmds - IRC-specific admin commands

=head1 SYNOPSIS

  ## Check current context name:
  !server current

  ## List contexts:
  !server list  

  ## Issue a (re)connect:
  !server connect Main
  
  ## Issue a disconnect:
  !server disconnect Alpha

=head1 DESCRIPTION

This is a L<Moo::Role> consumed by the default IRC plugin 
(L<Bot::Cobalt::IRC>). It provides basic administrative commands 
specific to IRC connection control.

As a failsafe you cannot disconnect from all contexts. See the C<die> 
command provided by L<Bot::Cobalt::Plugin::Master> instead.
However, Issuing a connect for a currently-connected context will 
attempt a reconnection (note that it's possible to 'lose' bots this way, 
particularly if the configuration or the remote server has changed in 
some way).

See the L</SYNOPSIS> for basic usage information; all commands require
SUPERUSER access.

=head1 AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

=cut