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?
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 one0
at a time. I also addedprint 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.