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

NAME

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

DESCRIPTION

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

This is the core plugin providing IRC functionality to Bot::Cobalt; incoming and outgoing IRC activity is handled just like any other plugin pipeline event.

This core IRC plugin provides a multi-server IRC interface via POE::Component::IRC. Subclasses or other IRC plugins should follow this pattern and provide a compatible event interface.

The IRC plugin does various work on incoming events we consider important enough to re-broadcast from the IRC component. This makes life easier on plugins and reduces code redundancy.

Arguments may vary by event. See below.

If you're trying to write Cobalt plugins, you probably want to start off with Bot::Cobalt::Manual::Plugins -- this is a reference for IRC-related events specifically.

EMITTED EVENTS

Connection state events

Bot_connected

Issued when an irc_001 (welcome msg) event is received.

Indicates the bot is now talking to an IRC server.

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

The relevant $core->Servers->{$context} object is updated prior to broadcasting this event. This means that 'maxmodes' and 'casemap' are now available for retrieval. You might use these to properly compare two nicknames, for example:

  ## grab eq_irc() from IRC::Utils
  ## also available if you "use Bot::Cobalt::Common;"
  ## see perldoc Bot::Cobalt::Common and Bot::Cobalt::Manual::Plugins
  use IRC::Utils qw/ eq_irc /;
  my $context = ${$_[0]};
  ## most servers are 'rfc1459', some may be ascii or -strict
  ## (some return totally fubar values, and we'll default to rfc1459)
  my $casemap = $core->Servers->{$context}->casemap;
  my $is_equal = IRC::Utils::eq_irc($nick_a, $nick_b, $casemap);

See Bot::Cobalt::IRC::Server for details on using a Server object.

Bot_disconnected

Broadcast when irc_disconnected is received.

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

$core->Servers->{$context}->connected will be false until a reconnect.

Bot_server_error

Issued on unknown ERROR : events. Not a socket error, but connecting failed.

The IRC component will provide a maybe-not-useful reason:

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

Incoming message events

Bot_public_msg

Broadcast upon receiving public text (text in a channel).

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

$msg is a Bot::Cobalt::IRC::Message object; in the case of public messages it is a Bot::Cobalt::IRC::Message::Public object.

See Bot::Cobalt::IRC::Message for complete documentation regarding available methods.

IMPORTANT: We don't automatically decode any character encodings. This means that text may be a byte-string of unknown encoding. Storing or displaying the text may present complications. You may want decode_irc from IRC::Utils for these applications. See "ENCODING" in IRC::Utils for more on encodings + IRC.

Also see:

Bot_public_cmd_CMD

Broadcast when a public command is triggered.

Plugins can catch these events to respond to specific commands.

CMD is the public command triggered; ie the first "word" of something like (if CmdChar is '!'): !kick --> Bot_public_cmd_kick

Syntax is precisely the same as "Bot_public_msg", with one major caveat: $msg->message_array will not contain the command.

This event is pushed to the pipeline before _public_msg.

Bot_private_msg

Broadcast when a private message is received.

Syntax is the same as "Bot_public_msg", except the first spotted destination is stored in key target

The default IRC interface plugins only spawn a single client per server. It's fairly safe to assume that target is the bot's current nickname.

Bot_got_notice

Broadcast when a /NOTICE is received.

Syntax is the same as "Bot_public_msg".

Bot_ctcp_action

Broadcast when a CTCP ACTION (/ME in most clients) is received.

Syntax is similar to "Bot_public_msg".

If the ACTION appears to have been directed at a channel, the channel method will return the same value as target -- otherwise it will return an empty string.

Sent notification events

Bot_message_sent

Broadcast when a PRIVMSG has been sent to the server via an event; in other words, when a 'message' event was sent.

Carries a copy of the target and text:

  my ($self, $core) = splice @_, 0, 2;
  my $context = ${$_[0]};
  my $target = ${$_[1]};
  my $string = ${$_[2]};

This being IRC, there is no guarantee that the message actually made it to its intended destination ;-)

Bot_notice_sent

Broadcast when a NOTICE has been sent out via a 'notice' event.

Same syntax as "Bot_message_sent".

Bot_ctcp_sent

Broadcast when a CTCP has been sent via a CTCP handler such as "action".

  my $context   = ${$_[0]};
  my $ctcp_type = ${$_[1]};  ## 'ACTION' for example
  my $target    = ${$_[2]};
  my $content   = ${$_[3]};

Bot_raw_sent

Broadcast when a raw string has been sent via "send_raw".

Arguments are the server context name and the raw string sent.

Channel state events

Bot_chan_sync

Broadcast when we've finished receiving channel status info. This generally indicates we're ready to talk to the channel.

Carries the channel name:

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

Bot_topic_changed

Broadcast when a channel topic has changed.

Carries a Bot::Cobalt::IRC::Event::Topic object:

  my ($self, $core) = @splice @_, 0, 2;
  my $topic_obj = ${$_[0]};
  
  my $context   = $topic_obj->context;
  my $setter    = $topic_obj->src_nick;
  my $new_topic = $topic_obj->topic;

Bot::Cobalt::IRC::Event::Topic is a subclass of Bot::Cobalt::IRC::Event and Bot::Cobalt::IRC::Event::Channel -- see the documentation for those base classes for complete details on available methods.

Note that the topic setter may be a server, just a nickname, the name of a service, or some other arbitrary string.

Bot_mode_changed

Broadcast when a channel mode has changed.

  my ($self, $core) = splice @_, 0, 2;
  my $modechg = ${$_[0]};
  
  my $context  = $modechg->context;
  my $modestr  = $modechg->mode;
  my $modeargs = $modechg->args;
  my $modehash = $modechg->hash;

The hashref returned by <-hash>> is produced by "parse_mode_line" in IRC::Utils.

It has two keys: modes and args. They are both ARRAY references:

  my @modes = @{ $modechg->hash->{modes} };
  my @args  = @{ $modechg->hash->{args}  };

If parsing failed, the hash is empty.

The trick to parsing modes is determining whether or not they have args that need to be pulled out. You can walk each individual mode and handle known types:

  MODE: for my $mode (@modes) {
    for ($mode) {
      next MODE if $mode =~ /[cimnpstCMRS]/; # oftc-hybrid/bc6 param-less modes
      if ($mode eq 'l') {  ## limit mode has an arg
        my $limit = shift @args;
        ...
      }
      if ($mode eq 'b') {
        ## shift off a ban ...
      }
      ## etc
    }
  }

Theoretically, you can find out which types should have args via ISUPPORT:

  my $irc = $self->Servers->{$context}->irc;
  my $is_chanmodes = $irc->isupport('CHANMODES')
                     || 'b,k,l,imnpst';  ## oldschool set
  ## $m_list modes add/delete modes from a list (bans for example)
  ## $m_always modes always have a param specified.
  ## $m_only modes only have a param specified on a '+' operation.
  ## $m_never will never have a parameter.
  my ($m_list, $m_always, $m_only, $m_never) = split ',', $is_chanmodes;
  ## get status modes (ops, voice ...)
  ## allegedly not all servers report all PREFIX modes
  my $m_status = $irc->isupport('PREFIX') || '(ov)@+';
  $m_status =~ s/^\((\w+)\).*$/$1/;

See http://www.irc.org/tech_docs/005.html for more information on ISUPPORT.

As of this writing the Cobalt core provides no convenience method for this.

Bot_user_joined

Broadcast when a user joins a channel we are on.

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

Carries a Bot::Cobalt::IRC::Event::Channel object.

Bot_self_joined

Broadcast when the bot joins a channel.

A "Bot_user_joined" is still issued as well.

Carries the same syntax and caveats as "Bot_self_left" (below)

Bot_user_left

Broadcast when a user parts a channel we are on.

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

Carries a Bot::Cobalt::IRC::Event::Channel object; also see "Bot_user_joined".

Bot_self_left

Broadcast when the bot parts a channel, possibly via coercion.

A plugin can catch Bot_part events to find out that the bot was told to depart from a channel. However, the bot may've been forced to PART by the IRCD. Many daemons provide a 'REMOVE' and/or 'SAPART' that will do this. Bot_self_left indicates the bot left the channel, as determined by matching the bot's nickname against the user who left.

${$_[1]} is the channel name.

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

IMPORTANT:

Uses eq_irc with the server's CASEMAPPING to determine whether this is actually the bot leaving, in order to cope with servers issuing forced parts with incorrect case.

This method may be unreliable on servers with an incorrect CASEMAPPING in ISUPPORT, as it will fall back to normal rfc1459 rules.

Also see "Bot_user_left"

Bot_self_kicked

Broadcast when the bot was seemingly kicked from a channel.

  my ($self, $core) = splice @_, 0, 2;
  my $context = ${$_[0]};
  my ($src, $chan, $reason) = (${$_[1]}, ${$_[2]}, ${$_[3]});

Relies on the same logic as "Bot_self_left" -- be sure to read the note in that section (above).

The bot will probably attempt to auto-rejoin unless configured otherwise.

Bot_user_kicked

Broadcast when a user (maybe us) is kicked.

  my ($self, $core) = splice @_, 0, 2;
  my $kick    = ${$_[0]};
  
  my $kicked    = $kick->kicked;
  my $kicked_by = $kick->src_nick;
  my $reason    = $kick->reason;

Carries a Bot::Cobalt::IRC::Event::Kick object.

Bot_invited

Broadcast when the bot has been invited to a channel.

  my ($self, $core) = splice @_, 0, 2;
  my $invite  = ${$_[0]};
  
  my $invited_to = $invite->channel;
  my $invited_by = $invite->src_nick;

Carries a Bot::Cobalt::IRC::Event::Channel object.

User state events

Bot_umode_changed

Broadcast when mode changes on the bot's nickname (umode).

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

  my $modestr = $modechg->mode;

Carries a Bot::Cobalt::IRC::Event::Mode object.

Bot_nick_changed

Broadcast when a visible user's nickname changes.

  my ($self, $core) = splice @_, 0, 2;
  my $nchange = ${$_[0]};
  
  my $old = $nchange->old_nick;
  my $new = $nchange->new_nick;

  if ( $nchange->equal ) {
    ## Case change only
  }

Carries a Bot::Cobalt::IRC::Event::Nick object.

equal is determined by attempting to get server CASEMAPPING= (falling back to 'rfc1459' rules) and using IRC::Utils to check whether this appears to be just a case change. This method may be unreliable on servers with an incorrect CASEMAPPING value in ISUPPORT.

Bot_self_nick_changed

Broadcast when our own nickname changed, possibly via coercion.

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

Bot_user_quit

Broadcast when a visible user has quit.

We can only receive quit notifications if we share channels with the user, of course.

  my ($self, $core) = splice @_, 0, 2;
  my $quit = ${ $_[0] };
  
  my $nickname = $quit->src_nick;
  my $reason   = $quit->reason;

Carries a Bot::Cobalt::IRC::Event::Quit object.

Outgoing message triggers

It's possible to write plugins that register for USER events to catch messages before they are dispatched to IRC.

These events are prefixed with Outgoing_.

Using this mechanism, you can write output filters by registering for a USER event:

  ## in your Cobalt_register, perhaps:
  $core->plugin_register( $self, 'USER',
    [ 'message' ],
  );
  
  ## handler:
  sub Outgoing_message {
    my ($self, $core) = splice @_, 0, 2;
    my $context = ${ $_[0] };
    my $target  = ${ $_[1] };
    
    ## You can modify these references directly.
    ## This is the same as Plugin::OutputFilters::StripFormat:
    ${ $_[2] } = strip_formatting( ${ $_[2] } );
    
    ## If you EAT_ALL, the message won't be sent:
    return PLUGIN_EAT_NONE
  }

The following USER events are emitted:

Outgoing_message

Syndicated when a "message" event has been received.

Event arguments are references to the context, target, and message string, respectively.

Outgoing_notice

Syndicated when a "notice" event has been received; arguments are the same as "Outgoing_message".

Outgoing_ctcp

Syndicated when a CTCP is about to be sent via "action" or a similar CTCP handler.

  my $context   = ${ $_[0] };
  my $ctcp_type = ${ $_[1] };
  my $target    = ${ $_[2] };
  my $content   = ${ $_[3] };

ACCEPTED EVENTS

Outgoing IRC commands

message

A message event for our context triggers a PRIVMSG send.

  broadcast( 'message', $context, $target, $string );

An "Outgoing_message" USER event will be issued prior to sending.

Upon completion a "Bot_message_sent" event will be broadcast.

notice

A notice event for our context triggers a NOTICE.

  broadcast( 'notice', $context, $target, $string );

An "Outgoing_notice" USER event will be issued prior to sending.

Upon completion a "Bot_notice_sent" event will be broadcast.

action

A action event sends a CTCP ACTION (also known as '/me') to a channel or nickname.

  broadcast( 'action', $context, $target, $string );

An "Outgoing_ctcp" USER event will be issued prior to sending.

Upon completion a "Bot_ctcp_sent" event will be broadcast.

send_raw

Quote a raw string to the IRC server.

Does no parsing or sanity checking.

  broadcast( 'send_raw', $raw );

A "Bot_raw_sent" will be broadcast.

mode

A mode event for our context attempts a mode change.

Typically the target will be either a channel or the bot's own nickname.

  broadcast( 'mode', $context, $target, $modestr );
  ## example for Main context:
  broadcast( 'mode', 'Main', '#mychan', '+ik some_key' );

This being IRC, there is no guarantee that the bot has privileges to effect the changes, or that the changes took place.

topic

A topic event for our context attempts to change channel topic.

  broadcast( 'topic', $context, $channel, $new_topic );

kick

A kick event for our context attempts to kick a user.

A reason can be supplied:

  broadcast( 'kick', $context, $channel, $target, $reason );

join

A join event for our context attempts to join a channel.

  broadcast( 'join', $context, $channel );

Catch "Bot_chan_sync" to check for channel sync status.

part

A part event for our context attempts to leave a channel.

A reason can be supplied:

  broadcast( 'part', $context, $channel, $reason );

There is no guarantee that we were present on the channel in the first place.

Server control

ircplug_connect

Takes the name of a configured context.

Attempts to initialize a connection for the named context (if the configuration can be found).

This interface is evolving and subject to change.

ircplug_disconnect

Same arguments and caveats as ircplug_connect.

SEE ALSO

AUTHOR

Jon Portnoy <avenj@cobaltirc.org>