Bot-Cobalt

 view release on metacpan or  search on metacpan

lib/Bot/Cobalt/IRC.pm  view on Meta::CPAN

  Component::IRC::Plugin::CTCP
  Component::IRC::Plugin::AutoJoin
  Component::IRC::Plugin::Connector
  Component::IRC::Plugin::NickServID
  Component::IRC::Plugin::NickReclaim
/;

## Bot::Cobalt::Common pulls the rest of these:
use IRC::Utils 'parse_mode_line';


use Moo;

has NON_RELOADABLE => (
  ## Well, really, it's sort-of unloadable.
  ##  ... but life usually sucks when you do.
  ## Call _set_NON_RELOADABLE if you really need to
  isa       => Bool,
  is        => 'rwp',
  default   => sub { 1 },
);

## We keep references to our ircobjs; core tracks these also,
## but there is no guarantee that we're the only IRC plugin loaded.
has ircobjs => (
  lazy      => 1,
  is        => 'rw',
  isa       => HashObj,
  coerce    => 1,
  default   => sub { {} },
);

has flood => (
  is        => 'ro',
  isa       => Object,
  lazy      => 1,
  predicate => 'has_flood',
  default   => sub {
    my $ccfg  = core->get_core_cfg;
    my $count = $ccfg->opts->{FloodCount} || 5;
    my $secs  = $ccfg->opts->{FloodTime}  || 6;
    Bot::Cobalt::IRC::FloodChk->new(
      count => $count,
      in    => $secs,
    )
  },
);

## Outgoing IRC traffic is handled by UserEvents role:
with 'Bot::Cobalt::IRC::Role::UserEvents';

## Administrative commands:
with 'Bot::Cobalt::IRC::Role::AdminCmds';

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

  register($self, SERVER => 'all' );
  broadcast( 'initialize_irc' );

  ## Start a lazy cleanup timer for flood->expire
  $core->timer_set( 180,
    +{ Event => 'ircplug_chk_floodkey_expire' },
    'IRCPLUG_CHK_FLOODKEY_EXPIRE'
  );

  logger->info("Loaded");

  PLUGIN_EAT_NONE
}

sub Cobalt_unregister {
  my ($self, $core) = splice @_, 0, 2;
  logger->info("Unregistering and dropping servers.");
  $self->_clear_context($_) for keys %{ $self->ircobjs };
  logger->debug("Clean unload");
  PLUGIN_EAT_NONE
}

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

  ## The IRC: directive in cobalt.conf provides context 'Main'
  ## (This will override any 'Main' specified in multiserv.conf)
  ## Munge core->irc() hash into our plugin's opts()
  my $p_cfg = $core->cfg->plugins->plugin( plugin_alias($self) );
  $p_cfg->opts->{Networks}->{Main} = $core->cfg->core->irc;

  if (exists $p_cfg->opts->{Networks}->{'-ALL'}) {
    ## Reserved by core Auth plugin
    logger->error("-ALL is not a valid context name, disregarding.");
    delete $p_cfg->opts->{Networks}->{'-ALL'}
  }

  my $active_contexts = 0;
  for my $context (keys %{ $p_cfg->opts->{Networks} } ) {
    ++$active_contexts;
    next if defined $p_cfg->opts->{Networks}->{$context}->{Enabled}
         and $p_cfg->opts->{Networks}->{$context}->{Enabled} == 0;
    logger->debug("Found configured context $context");
    broadcast( 'ircplug_connect', $context );
  }

  unless ($active_contexts) {
    logger->error("No contexts configured/enabled!");
  }
  logger->info("Connecting to $active_contexts contexts");
  
  PLUGIN_EAT_ALL
}

sub Bot_ircplug_connect {
  my ($self, $core) = splice @_, 0, 2;
  my $context = ${ $_[0] };

  ## Spawn an IRC Component and a Session to manage it.
  ##
  ## Called for each configured context.
  ##
  ## The sessions call the same object with different contexts in HEAP;
  ## the handlers do some processing and relay the event from the
  ## PoCo::IRC syndicator to the Bot::Cobalt::Core pipeline.

lib/Bot/Cobalt/IRC.pm  view on Meta::CPAN

  broadcast( 'user_quit', $quit );
}

sub irc_snotice {
  my ($self, $heap, $kernel) = @_[OBJECT, HEAP, KERNEL];

  my $context = $heap->{Context};

  ## These are weird.
  ## There should be at least a string.
  my ($string, $target, $sender) = @_[ARG0 .. ARG2];

  ## FIXME test / POD
  broadcast( 'server_notice', $context, $string, $target, $sender );
}

sub irc_topic {
  my ($self, $kernel, $heap) = @_[OBJECT, KERNEL, HEAP];
  my ($src, $channel, $topic) = @_[ARG0 .. ARG2];

  my $context = $heap->{Context};
  my $irc     = $self->ircobjs->{$context};

  my $topic_obj = Bot::Cobalt::IRC::Event::Topic->new(
    context => $context,
    src     => $src,
    channel => $channel,
    topic   => $topic,
  );

  ## Bot_topic_changed
  broadcast( 'topic_changed', $topic_obj );
}


### Internals.

sub Bot_rehashed {
  my ($self, $core) = splice @_, 0, 2;
  my $type = ${ $_[0] };

  if ($type eq 'core' || $type eq 'channels') {
    logger->info("Rehash received ($type), resetting ajoins");
    $self->_reset_ajoins;
  }

  ## FIXME nickservid rehash if needed

  return PLUGIN_EAT_NONE
}

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

  ## Lazy flood tracker cleanup.
  ## These are just arrays of timestamps, but they gotta be cleaned up
  ## when they're stale.

  $self->flood->expire if $self->has_flood;

  $core->timer_set( 60,
    { Event => 'ircplug_chk_floodkey_expire' },
    'IRCPLUG_CHK_FLOODKEY_EXPIRE'
  );

  return PLUGIN_EAT_ALL
}

sub Bot_ircplug_flood_rem_ignore {
  my ($self, $core) = splice @_, 0, 2;
  my $context = ${ $_[0] };
  my $mask    = ${ $_[1] };
  ## Internal timer-fired event to remove temp ignores.

  logger->info("Clearing temp ignore: $mask ($context)");

  $core->ignore->del( $context, $mask );

  broadcast( 'flood_ignore_deleted', $context, $mask );

  return PLUGIN_EAT_ALL
}

sub flood_ignore {
  ## Pass me a context and a mask
  ## Set a temporary ignore and a timer to remove it
  my ($self, $context, $mask) = @_;

  my $corecf = core->get_core_cfg;
  my $ignore_time = $corecf->opts->{FloodIgnore} || 20;

  $self->flood->clear($context, $mask);

  logger->info(
    "Issuing temporary ignore due to flood: $mask ($context)"
  );

  my $added = core->ignore->add(
    $context, $mask, "flood_ignore", __PACKAGE__
  );

  broadcast( 'flood_ignore_added', $context, $mask );

  core->timer_set( $ignore_time,
    {
      Event => 'ircplug_flood_rem_ignore',
      Args  => [ $context, $mask ],
    },
  );
}

sub _reset_ajoins {
  my ($self) = @_;

  my $corecf  = core->get_core_cfg;
  my $servers = core->Servers;

  CONTEXT: for my $context (keys %$servers) {
    my $chanscf = core->get_channels_cfg($context) // {};

    my $irc = core->get_irc_obj($context) || next CONTEXT;

    my %ajoin;

    CHAN: for my $channel (keys %$chanscf) {
      my $key = $chanscf->{$channel}->{password} // '';
      $ajoin{$channel} = $key;
    }

    logger->debug("Removing AutoJoin plugin for $context");
    $irc->plugin_del('AutoJoin');

    logger->debug("Loading new AutoJoin plugin for $context");
    $irc->plugin_add('AutoJoin' =>
      POE::Component::IRC::Plugin::AutoJoin->new(
        Channels => \%ajoin,
        RejoinOnKick => $corecf->opts->{Chan_RetryAfterKick} // 1,
        Rejoin_delay => $corecf->opts->{Chan_RejoinDelay}    // 5,
        NickServ_delay    => $corecf->opts->{Chan_NickServDelay} // 1,
        Retry_when_banned => $corecf->opts->{Chan_RetryAfterBan} // 60,
      ),
    );

  }

}

1;
__END__


=pod

=head1 NAME

Bot::Cobalt::IRC -- Bot::Cobalt IRC bridge

=head1 DESCRIPTION

For a description of the commands provided by the IRC bridge, see 
L<Bot::Cobalt::IRC::Role::AdminCmds>.

This is the core plugin providing IRC functionality to 
L<Bot::Cobalt>; incoming and outgoing IRC activity 



( run in 0.587 second using v1.01-cache-2.11-cpan-437f7b0c052 )