How can I implement a simple IRC client in Perl?
Asked Answered
C

4

5

I'm working on a tool that needs to send IRC messages to an internal IRC channel. This isn't a constantly running program, but rather a tool that will be invoked occasionally and needs to be able to notify the channel with a couple of messages when it's invoked.

I looked at Net::IRC, but it's been dead since 2004. So I looked at the alternatives that it lists (Bot::BasicBot and POE::Component::IRC) but both of those need to be run under POE and its event loop. The same problem occurs for something like Net::Async::IRC since it needs to be run in the IO::Async event loop.

I'm not writing a full fledged bot that needs to interact with anything, I just want to login to an irc server, join a channel, post some quick messages and then leave. I don't want to have to re-write this whole program to fit inside of some framework's event loop just to do this.

So, any recommendations for a library for a simple IRC client that won't make me rewrite my whole app?

Coexecutor answered 30/9, 2010 at 21:34 Comment(1)
POE may be a little heavyweight, but AnyEvent::IRC should be able to be run completely in the context of a function. Not sure if IO::Async is able to do thatCoherent
C
7

Use AnyEvent::IRC::Client, while it is using the AnyEvent 'event loop', you don't have to rewrite your app to use it, you can do something like this:

use AnyEvent;
use AnyEvent::IRC::Client;

with the rest of your module use lines; and something like this

sub do_irc {
    my $log_chan = '#foobar';
    my $timer; 
    my $c = AnyEvent->condvar;
    my $con = new AnyEvent::IRC::Client;

    $con->reg_cb( join => sub {
        my ($con, $nick, $channel, $is_myself) = @_; 
        if ($is_myself && $channel eq $log_chan) {
           $con->send_chan( $channel, PRIVMSG => ($channel, 'my information') );
           $timer = AnyEvent->timer ( 
                after => 1,
                cb => sub {
                    undef $timer;
                    $con->disconnect('done');
                });
        }
    });

    $con->connect($irc_serv, 6667, { nick => $your_nick } );
    $con->send_srv( JOIN => ($log_chan) );
    $c->wait;
    $con->disconnect;

}

to do the connection, it will only return once it is done (error handling left out for brevity). Nothing else in your app needs to use AnyEvent.

Coherent answered 30/9, 2010 at 22:16 Comment(4)
This is close to what I'm looking for except that I'd like to avoid having to connect and disconnect on each message. Each run of this tool might take a couple of minutes and I want to send a message a various points in the process, like a progress update. Any way to do that without rewriting to use the event loop?Coexecutor
The problem with that is that the IRC server is going to disconnect you if you don't reply promptly to IRC PING requests, which is handled by the event loop. You can add calls to AnyEvent::one_event in your code, but that will block until you get incoming IRC data (or a timer expires).Coherent
I think this code is missing something. It seems like the $c->wait (shouldn't that be $c->recv?) call needs a corresponding $c->send call or it would hang foreverCoexecutor
So that my main program wouldn't block of have to connect/disconnect for every message I decided to go with a multi-process fork where the parent goes about it's work but sends messages via a pipe to the child that uses AnyEvent::IRC::Client to send the messages to the IRC channel. Thanks for the inspiration for this.Coexecutor
S
5

Net::IRC is still perfectly usable, if all you want to do is something as simple as you're describing, I think it would do just fine.

That said, getting used to POE isn't a bad idea at all, especially since IRC bots tend to grow in functionality over time :)

Scalage answered 30/9, 2010 at 21:37 Comment(0)
V
3

Note that there's actually nothing stopping you from using PoCo-IRC or BasicBot. Yes, they run under POE, but POE doesn't have to have control over your entire app.

If you're just connecting, doing some stuff on IRC, disconnecting, and doing other things, you can just make sure that the IRC session destroys itself when it's done -- when there are no sessions remaining, POE::Kernel->run will return control right back to your program.

If you're dealing with a longer-lived connection, but you still want to turn the control around, POE provides run_one_timeslice and run_while methods that give you fine control over when and where POE runs. Of course, you have to arrange for it to run at least often enough to respond to any server PINGs and keep the socket buffers from filling up.

Actually, Net::IRC does exactly the same thing in taking over your app with its own event loop -- except it's not a named thing like POE or AnyEvent -- it's just "the Net::IRC event loop". It doesn't mean you need a total rewrite to work with Net::IRC, and it doesn't mean you need a total rewrite to work with POE :)

Volplane answered 30/9, 2010 at 23:30 Comment(0)
B
1

Using the answer from MkV, I was able to create a working command-line script:

#!/usr/bin/perl

use strict;
use warnings;
use AnyEvent;
use AnyEvent::IRC::Client;
use Data::Dumper ();
use Getopt::Long;

my %opt = (
    channel => '#ircmsgtest',
    nick    => "ircmsg$$",
    port    => 6667,
    server  => 'irc.freenode.net',
    verbose => undef,
);

GetOptions(\%opt,'channel','nick', 'port', 'server', 'verbose|v');
my $message = shift() || "test message @{[ scalar localtime() ]}";
if ($opt{verbose}) {
    warn "message is: '$message'";
    warn Data::Dumper->Dump([\%opt], [qw(*opt)]);
}

my $c = AnyEvent->condvar;
my $con = AnyEvent::IRC::Client->new;

$con->reg_cb(
    join => sub {
        my ($con, $nick, $channel, $is_myself) = @_; 
        if ($is_myself && $channel eq $opt{channel}) {
            $con->send_chan($channel, PRIVMSG => $channel, $message);
            $c->send;
        }
    }
);

$con->connect($opt{server}, $opt{port}, { nick => $opt{nick} } );
$con->send_srv(JOIN => $opt{channel});
$c->wait;
$con->disconnect;

It connects, sends the message, and then disconnects, which is perfect for my needs.

Bromide answered 11/2, 2011 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.