If you're just starting out and would like to write a bot without worrying about reconnecting, take a look at one of these CPAN modules:

Both handle server reconnection for you, and both use a method similar to the one in this recipe.

[POE::Component::IRC::Plugin::Connector] implements this code as a handy plugin -- BinGOs

This code is extracted from one of my existing bots. It is not a complete program. While it works in my bot, the version here has not been tested. Please feel free to make corrections.

A robust "reconnect" algorithm has evolved over time. Currently it can be summarized in three rules:

That last rule is important. Without it, your bot may take hours to notice that a server connection has gone away. It will look as if the bot has hanged. Research TCP keep-alives if you'd like to know why.

POE::Component::IRC provides three events that signal imminent (or recent) disconnection:

All of them can be routed to the same "reconnect" handler. We use a slightly different POE::Session constructor syntax than most bots. It lets us handle multiple events with the same function.

POE::Session->create(
  inline_states => {
    irc_disconnected => \&bot_reconnect,
    irc_error        => \&bot_reconnect,
    irc_socketerr    => \&bot_reconnect,
    connect          => \&bot_connect,
    _start           => \&bot_start,
    autoping         => \&bot_do_autoping,
    irc_001          => \&bot_connected,
  },
);

# Initialize the bot, and connect to a server.
sub bot_start {
  my ($kernel, $heap) = @_[KERNEL, HEAP];

  # Set up the bot here.
  ...;

  # Connect.
  $kernel->yield("connect");
}

# The connect code is in its own handler so it can be reached from two
# places: _start, and bot_reconnect.
sub bot_connect {
  my ($kernel, $heap) = @_[KERNEL, HEAP];

  # Consider performing server rotation here.  A server may not be
  # back right away, and having alternatives means your bot will be
  # back in business sooner.
  ...;

  # Do the connection.
  $kernel->post(poco_irc => connect => \%parameters);
}

# Once connected, start a periodic timer to ping ourselves.  This
# ensures that the IRC connection is still alive.  Otherwise the TCP
# socket may stall, and you won't receive a disconnect notice for
# up to several hours.
sub bot_connected {
  my ($kernel, $heap) = @_[KERNEL, HEAP];

  # Join channel(s), set user modes, etc.
  ...;
  $heap->{seen_traffic} = 1;
  $kernel->delay(autoping => 300);
}

# Ping ourselves, but only if we haven't seen any traffic since the
# last ping.  This prevents us from pinging ourselves more than
# necessary (which tends to get noticed by server operators).
sub bot_do_autoping {
  my ($kernel, $heap) = @_[KERNEL, HEAP];
  $kernel->post(poco_irc => userhost => "my-nickname")
    unless $heap->{seen_traffic};
  $heap->{seen_traffic} = 0;
  $kernel->delay(autoping => 300);
}

# Reconnect in 60 seconds.  Don't ping while we're disconnected.  It's
# important to wait between connection attempts or the server may
# detect "abuse".  In that case, you may be prohibited from connecting
# at all.
sub bot_reconnect {
  my $kernel = $_[KERNEL];
  $kernel->delay(autoping => undef);
  $kernel->delay(connect  => 60);
}

Finally, be sure to set $heap->{seen_traffic} = 1; from the bot's other IRC event handlers. This will help keep the bot quiet, which means it's more likely to be tolerated by IRC server operators.


The new $_[KERNEL]->delay() stuff may be usefull for the reconnect pings, as you wouldn't need to maintain any global flags. Basically, you set the ping timeout to X amount of seconds, and then just reset the timer every time you have an action. Thus simplifying your ping code (don't need to check anything, just fire off the ping).

Probably most easily implemented inside a sub (reset_ping() or somesuch), to keep from having to type out the whole ugly delay() line each time. --Azhrarn