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


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.


Connection state events


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.


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.


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


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:


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.


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.


Broadcast when a /NOTICE is received.

Syntax is the same as "Bot_public_msg".


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


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 ;-)


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

Same syntax as "Bot_message_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]};


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


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]};


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.


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 for more information on ISUPPORT.

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


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.


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)


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".


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]};


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"


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.


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.


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


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.


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.


Broadcast when our own nickname changed, possibly via coercion.

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


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:


Syndicated when a "message" event has been received.

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


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


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] };


Outgoing IRC commands


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.


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.


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.


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.


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.


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

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


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

A reason can be supplied:

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


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.


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


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.


Same arguments and caveats as ircplug_connect.



Jon Portnoy <>