http://www.perlmonks.org?node_id=1002402

Contraversy has asked for the wisdom of the Perl Monks concerning the following question:

Ok, I hope I am asking this in the correct place. I have a simple IRC Client that I want to make, there are a plethora of code snippets online for this. And I have 1/2 of a working client. It connects, responds to the PING requests and can even auto reply to any commands I want it to recognize and reply to. But my problem is, I don't want a bot that idles or auto-replies. I want a basic, bare-bones client that can send messages as well as receive them. If I try to add a loop/command to send a message it stops the entire loop and waits for me to input. I tried this in C++ using a non-blocking input command and still couldn't send any information. My Main Question, do I have to open an extra socket? Simply for sending messages that I input?
#!/usr/local/bin/perl -w # irc.pl # A simple IRC robot. # Usage: perl irc.pl use strict; # We will use a raw socket to connect to the IRC server. use IO::Socket; # The server to connect to and our details. my $server = "irc.servercentral.net"; my $nick = "opti__"; my $login = "opti__"; # The channel which the bot will join. my $channel = "#contraversy"; # Connect to the IRC server. my $sock = new IO::Socket::INET(PeerAddr => $server, PeerPort => 6667, Proto => 'tcp') or die "Can't connect\n"; # Log on to the server. print $sock "NICK $nick\r\n"; print $sock "USER $login 8 * :contraIRC\r\n"; # Read lines from the server until it tells us we have connected. while (my $input = <$sock>) { # Check the numerical responses from the server. if ($input =~ /004/) { # We are now logged in. last; } elsif ($input =~ /433/) { die "Nickname is already in use."; } } # Join the channel. print $sock "JOIN $channel\r\n"; # Keep reading lines from the server. while (my $input = <$sock>) { chop $input; if ($input =~ /^PING(.*)$/i) { # We must respond to PINGs to avoid being disconnected. print $sock "PONG $1\r\n"; } else { # Print the raw line received by the bot. print "$input\n"; } }
PS if you run this and try to send a message its in the RFC format "PRIVMSG #channelornick :message here \r\n" <-- The CRLF is required also. Any help will be appreciated! Thanks!

Replies are listed 'Best First'.
Re: Very Basic Perl Question about IRC
by Tanktalus (Canon) on Nov 06, 2012 at 01:48 UTC

    There are a few approaches available here. The most obvious might be to use threads - one thread that would read from the socket (blocking), a second thread to write to the socket (blocks on the input queue used to feed it stuff to send), and a third to read from stdin (perhaps using Term::ReadLine). I'm not sure if that middle thread is quite needed, though it would handle race conditions between the first and last threads for sending stuff to the IRC server.

    Personally, I do this basic type of thing with a single thread using event-based code, usually AnyEvent/Coro, though some will swear by POE. And, with a new enough Term::ReadLine, you can integrate it with Coro fairly well, or other event loops not quite so well, but still passably if done with Term::ReadLine being your main event loop (see Term::ReadLine::Event for example code), though there's always the issue of spitting stuff to the screen (input from IRC server) while waiting at a prompt.

    That's for "this basic type of thing." For this specific type of thing (IRC), I use Xchat and perl plugins to Xchat.

      Thank you so much, more than enough info here to do what I need!
Re: Very Basic Perl Question about Sockets .. and IRC
by zentara (Archbishop) on Nov 06, 2012 at 10:47 UTC
    I want a basic, bare-bones client that can send messages as well as receive them.

    Try this basic forking client code.

    #!/usr/bin/perl -w use strict; use IO::Socket; my ( $host, $port, $kidpid, $handle, $line ); ( $host, $port ) = ('localhost',1200); my $name = shift || ''; if($name eq ''){print "What's your name?\n"} chomp ($name = <>); # create a tcp connection to the specified host and port $handle = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port ) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # split the program into two processes, identical twins die "can't fork: $!" unless defined( $kidpid = fork() ); # the if{} block runs only in the parent process if ($kidpid) { # copy the socket to standard output while ( defined( $line = <$handle> ) ) { print STDOUT $line; } kill( "TERM", $kidpid ); # send SIGTERM to child } # the else{} block runs only in the child process else { # copy standard input to the socket while ( defined( $line = <STDIN> ) ) { print $handle "$name->$line"; } }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Very Basic Perl Question about IRC
by Anonymous Monk on Nov 06, 2012 at 01:40 UTC

    My Main Question, do I have to open an extra socket? Simply for sending messages that I input?

    What does the RFC say?