How can I get these Perl scripts to delay?

437 Views Asked by At

I'm making a simple IRC bot in Perl that can be used to "hunt ducks" in response to this IRC game bot. I'm doing this on a private scripting channel, irc.freenode.net ##duckhunt2 so as not to interfere with real people playing the game.

So far I've tried making a Perl bot using Net::IRC and a plugin for XChat, with my code here. The duck source bot sends a message like

・゜゜・。。・゜゜\_O< quack!

a random amount of time in between 8-60 minutes since the last duck was shot to let you know that a duck has arrived. You can then reply with .bang to shoot the duck and get one point added to your score. However, if you reply too quickly (within one second), it puts you in a 2 hour cooldown mode where you can't shoot any ducks. Sometimes it also throws in 7 second cooldowns because of "jammed guns" and such, as shown in line 272 of the game bot code.

Perl code

use Net::IRC;
use Time::HiRes qw(usleep nanosleep);

$ducksource = 'DUCK_SOURCE';
$server     = 'IRC_SERVER';
$channel    = 'IRC_CHANNEL';
$botnick    = 'BOT_NICKNAME';
$botnick2   = 'BOT_BACKUP_NICKNAME';
$password   = 'BOT_PASSWORD';
$botadmin   = 'BOT_ADMIN_NICKNAME';

$irc = new Net::IRC;

$conn = $irc->newconn(
    Nick     => $botnick,
    Server   => $server,
    Port     => IRC_SERVER_PORT,
    Username => $botnick
);

$conn->add_global_handler('376',        \&on_connect);
$conn->add_global_handler('disconnect', \&on_disconnect);
$conn->add_global_handler('kick',       \&on_kick);
$conn->add_global_handler('msg',        \&on_msg);
$conn->add_global_handler('public',     \&on_public);

$irc->start;

sub on_connect {

    $self = shift;

    $self->privmsg('nickserv', "identify $password");
    $self->join($channel);

    print "Connected\n";
}

sub on_disconnect {

    $self = shift;

    print "Disconnected, attempting to reconnect\n";
    $self->connect();
}

sub on_kick {

    $self = shift;

    $self->join($channel);
    $self->privmsg('nickserv', "/nick $botnick");
}

sub on_msg {

    $self  = shift;
    $event = shift;

    if ($event->nick eq $botadmin) {

        foreach $arg ($event->args) {

            if ($arg =~ m/uptime/) {
                $self->privmsg($botadmin, `uptime`);
            }
        }
    }
}

sub on_public {

    $self  = shift;
    $event = shift;

    if ($event->nick eq $ducksource) {

        foreach $arg ($event->args) {

            if (($arg =~ m/</) && ($arg !~ m/>/)) {
                usleep(250000);
                $self->privmsg($channel, ".bang");
            }

            if (   ($arg =~ m/missed/)
                || ($arg =~ m/jammed/)
                || ($arg =~ m/luck/)
                || ($arg =~ m/WTF/)) {
                $self->privmsg('nickserv', "/nick $botnick2");
                $self->privmsg($channel,   ".bang");
                $self->privmsg('nickserv', "/nick $botnick");
            }

            if (($arg =~ m/script/) || ($arg =~ m/period/)) {
                $self->privmsg('nickserv', "/nick $botnick2");
                $self->privmsg($channel,   ".bang");
            }
        }
    }
}

The Perl bot connects to the server, joins the chat room, and responds to a duck appearing, but I can't get it to delay the sending of the command .bang so that the game bot receives it after 1 second has passed and I don't go into the two-hour cooldown mode.

I know that the Perl sleep command only accept multiples of one second. I need to delay 0.25 seconds because it takes about 0.75 seconds for the message to reach the game bot, so I've tried using Time::HiRes and the usleep command, which uses microseconds (1,000 microseconds = 1 millisecond).

On line 61 of my code, I added usleep(250000) which should make the script pause for 0.25s before sending the message on the next line

$self->privmsg($channel, ".bang")

But the script does not wait -- it just sends the message as normal. It acts like it is ignoring the usleep command.

How can I fix this and make the bot wait before it sends the message?

Secondly, I'm confused over how to change nicknames. If the game bot gives me a 7 second cooldown, I'd like to quickly change my nick to another nick (e.g. HunterBot6000 to HunterBot6000_) shoot the duck (.bang), and change my nick back before another bot gets the duck. Typically you accomplish a nick change through the /nick NEWNICK command. However, I've tried sending this command to the channel and NickServ, and this doesn't change my nickname. How should I accomplish this?

I also tried writing an XChat plugin for the script to see if that would get rid of the timing issue, but that doesn't work either. After connecting to the server and joining the chat room in XChat, I load the plugin, and I have the same issue -- it responds to ducks with .bang but I cannot get it to wait before sending.

You can see the documentation Writing a simple XChat Perl Script. What am I doing wrong?

3

There are 3 best solutions below

0
On BEST ANSWER

Thank you for everyone's help. I was able to get the usleep command working and verify that it was delaying properly by changing the delay to a larger amount of seconds (e.g. usleep(25000000), 25 seconds) and then changing back to 0.25 seconds by removing one 0 at a time. I also added print Time::HiRes::time; before and after to verify that the delay was working. I also found that the proper command to change nicks is $self->nick($botnick2);, even though it is nowhere to be found in any Net::IRC documentation. Once again, thank you all for the help and advice.

0
On

I have also had trouble from usleep from Time::HiRes. This should effect a sleep of 250ms:

select(undef, undef, undef, 0.25);
1
On

You're asking multiple questions, but I can only answer one from my phone

You can change nicknames by sending

NICK newnick

Further information can be found in the RFC 2812.

However, Net::IRC might have more appropriate means for that.