Perl's AnyEvent::IRC::Client library does not close after sending messages

115 views Asked by At

I'm exploring the AnyEvent::IRC::Client library to send a list of messages to an IRC channel. I follow the sample code from its metacpan site (ref: https://metacpan.org/pod/AnyEvent::IRC::Client), except that I need to send a list of messages instead of a single message. So, I'm able to send all the messages in the array to the IRC channel successfully. The problem is that the Event loop does not close at the end (ie. I have to hit Ctrl+C to terminate the program). So any insight on how to close the event loop would be greatly appreciated. Thanks!

# Send some messages to IRC channel
use strict ;
use warnings 'all' ;
no warnings 'uninitialized' ;
use AnyEvent ;
use AnyEvent::IRC::Client ;
use Data::Dumper ;
sub say { print @_, "\n" }

my @messages = ( 'msg1','msg2','msg3','msg4','msg5','msg6','msg7','msg8','msg9','msg10' ) ;

sendIrc( 'ircServer', 6667, '#ircChannel1', 'user123', 'psswd', \@messages ) ;

sub sendIrc {#Params: ircServer(Str), port(Int), channel(Str), nick(Str), psswd(Str), messages(ArrRef<Str>) ; #Return: void ;
  my ( $server, $port, $chan, $nick, $psswd, $messages ) = @_ ;
  my $timer ;
  my $condVar = AnyEvent->condvar ;
  my $con = AnyEvent::IRC::Client->new ;
  
  $con->reg_cb( connect => sub {
    my ( $cond, $err ) = @_ ;
    if ( defined $err ) {
      warn "connect error: $err\n" ;
      return ;
    }#end if
  }) ;

  $con->reg_cb( registered => sub {
    say "User is in!" ;
  }) ;

  $con->reg_cb( disconnect => sub {
    say "User is out!" ;
  }) ;

  $con->reg_cb( sent => sub {
    my ( $con ) = @_ ;
    if ( $_[2] eq 'PRIVMSG' ) {
      say "Msg sent!" ;
      $timer = AnyEvent->timer (
        after => 1 ,
        cb    => sub {
          undef $timer ;
          $condVar->end ;
        }#end callback
      );#end timer
    }#end if
  }) ;

  $con->connect( $server, $port, { nick => $nick, password => $psswd } ) ;
  for my $msg ( @$messages ) {
    $condVar->begin ;
    $con->send_srv( PRIVMSG => ( $chan, $msg ) ) ;
  }#end for
  $condVar->wait ;
  $con->disconnect ;
  return ;
}#end sub
1

There are 1 answers

1
clamp On

The problem is, that AnyEvent::IRC::Client enqueues the messages and immediately emits the sent event. There seems to be no event that triggers when the messages are actually sent to the Server. Also it takes a certain time for each message to process. I modified your code to keep track of the number of active jobs and reset the timer accordingly. This is not a robust solution. Expect this to fail if the estimated time per job changes.

use strict ;
use warnings 'all' ;
no warnings 'uninitialized' ;
use AnyEvent ;
use AnyEvent::IRC::Client ;
use Data::Dumper ;
sub say { print @_, "\n" }
my @messages = map {"msg$_"} 0..10 ;

sendIrc( 'ircServer', 6667, '#ircChannel1', 'user123', 'psswd', \@messages ) ;

sub sendIrc {#Params: ircServer(Str), port(Int), channel(Str), nick(Str), psswd(Str), messages(ArrRef<Str>) ; #Return: void ;
  my ( $server, $port, $chan, $nick, $psswd, $messages ) = @_ ;
  my $timer;
  my $jobs = 0;# count of jobs
  my $time_per_job = 1;# seconds
  my $condVar = AnyEvent->condvar ;
  my $con = AnyEvent::IRC::Client->new ;
  my $cb = sub {shift->send("done\n")};
  $condVar->begin($cb); # one begin/end pair to wrap everything

  $con->reg_cb( connect => sub {
    my ( $cond, $err ) = @_ ;
    if ( defined $err ) {
      warn "connect error: $err\n" ;
      return ;
    }#end if
  }) ;

  $con->reg_cb( registered => sub {
                    say "User is in!" ;
  }) ;

  $con->reg_cb( disconnect => sub {
    say "User is out!" ;
  }) ;

  $con->reg_cb( sent => sub {
    if ( $_[2] eq 'PRIVMSG' ) {
        $jobs++;
        print "jobs waiting: $jobs\n";
        $timer = AnyEvent->timer (
            after => $jobs * $time_per_job ,
            cb    => sub{ while ($jobs){ $condVar->end;
                                         $jobs--;}
                      },#end callback
         );#end timer
    }#end if
  }) ;

  $con->connect( $server, $port, { nick => $nick, password => $psswd } ) ;

  for my $msg ( @$messages ) {
      $con->send_srv( PRIVMSG => ( $chan, $msg ) ) ;
      $condVar->begin;
  }#end for
  
  $condVar->end;
  my $isDone = $condVar->recv;
  print $isDone;
  $con->disconnect ;
  return ;
}#end sub