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

Greetings, fellow monks, long time no see.

I've been programming in other languages for a year or two, so I'm a little bit confused about Perl now. I was wondering if you can help me.

I'm developing an IRC bot using Net::IRC and (I must confess) for the first time I'm really doing it by the book, wich means I'm using strict and the -w flag :)

SO, In the beginning of my script I declare an array of bot masters:
my @masters = ("John","Paul","George","Ringo");

and later I have a sub that checks if a nickname that is typing to the bot is a master:
sub is_master { my $who = $_[0]; my $grant = 0; foreach my $master (@masters) { if ($who eq $master) #Here's the line 148 { $grant++; } } if ($grant > 0) { return 1; } else { return 0; } }

and another sub that handles the commands:
sub do_command { my ($conn, $event, $com) = @_; my $nick = $event->{nick}; if ($com eq "!rh") { if (is_master($nick)) #I don't have problems here { do_runlog(2, $event->{nick}); $conn->quit("Resetando..."); start_it(); } } elsif ($com eq "!sh") { if (is_master($nick)) #and here { do_runlog(3, $event->{nick}); $conn->quit("Saindo..."); } } elsif ($com =~ /^!ak/) { my ($scom, $p1, $p2) = split(/ /,$com); $conn->sl("MODE #$canal +b $p1"); $conn->sl("KICK #$canal $p1 $p2"); do_kick($p1); } elsif ($com =~ /^!dk/) { my ($scom, $p1, $p2) = split(/ /,$com); $conn->sl("MODE #$canal -b $p1"); if ($p2 ne "") { $conn->privmsg($p1, $p2); } undo_kick($p1); } elsif ($com eq "!help" || $com eq "!ajuda") { do_help($conn,$nick); } elsif ($com eq "!dig") { my ($scom, $p1) = split(/ /,$com); undo_ignore($conn,$event,$p1); } }

I know that the problem starts at the do_help($conn,$nick); line, so here's the do_help sub:
sub do_help { my ($conn, $nick) = @_; $conn->privmsg($nick, "LoBOT help:"); $conn->privmsg($nick, "------------------------------------------- +-----------------"); $conn->privmsg($nick, "Todos os comandos são iniciados por um pont +o de exclamação."); $conn->privmsg($nick, "O que está entre colchetes - [] - é opciona +l (os colchetes não devem ser digitados)."); $conn->privmsg($nick, "!ak nick [motivo] - adiciona um usuário no +akick do bot e kicka ele do canal com o motivo digitado"); $conn->privmsg($nick, "!dk nick [mensagem] - deleta um usuário do +akick do bot e caso mensagem tenha sido digitado, envia-a para o mesm +o por pvt."); $conn->privmsg($nick, "!help ou !ajuda - exibe este texto"); if (is_master($nick)) #Here's the line that calls the is_master su +b and starts my problem { $conn->privmsg($nick, "Os comandos válidos para os masters são +:"); $conn->privmsg($nick, "!dig nick - Deleta um usuário da lista +de ignores do bot"); $conn->privmsg($nick, "!sh - desativa o bot (Apenas para maste +rs)"); $conn->privmsg($nick, "!rh - reseta o bot (Apenas para masters +)"); } $conn->privmsg($nick, "------------------------------------------- +-----------------"); $conn->privmsg($nick, "Fim do help."); }

Well, problem is that when I check the error log it says this:

Use of uninitialized value in string eq at C:\foobot\foobot.pl line 148 (I've marked this line in the is_master sub above).

The (not so) funny thing is that I have an identical sub that checks if a nickname is a chanop and it works pretty fine, withou yelling any errors. AND I have other calls to the is_master sub (marked above) that don't generate error as well...

Can you please help me? Thanks in advance,

my ($author_nickname, $author_email) = ("DaWolf","erabbott\@terra.com.br") if ($author_name eq "Er Galvão Abbott");

Replies are listed 'Best First'.
Re: Help with strict
by hardburn (Abbot) on Oct 28, 2003 at 19:09 UTC

    I'm not entirely sure what the problem is, but you could improve your datastructure and get your is_master() sub a lot shorter (or even ditch it entirely). Instead of an array, put your bot masters in a hash:

    my %masters = ( John => 1, Paul => 1, George => 1, Ringo => 1, );

    And then replace all calls to is_master() with exists $masters{$name}.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    :(){ :|:&};:

    Note: All code is untested, unless otherwise stated

      Hmmm not a solution, but a cool idea.

      Thanks, hardburn, I'll try that.

      BTW, the only problem is that doesn't solve my strict handicap and I wanna be a nice Perl programmer and not the evil one I've used to be *lol*

      my ($author_nickname, $author_email) = ("DaWolf","erabbott\@terra.com.br") if ($author_name eq "Er Galvão Abbott");
Re: Help with strict
by graff (Chancellor) on Oct 29, 2003 at 01:22 UTC
    I think davido's reply is on the right track. To put that in other terms, consider the problem as follows:
    • Starting with the call to do_command(), you set $nick to be the value provided by $event->{nick}, but you don't test to see if this value is "defined" or non-empty
    • You pass $nick to do_help(), and this sub doesn't check whether $nick is defined either -- all it does is...
    • Pass the value on to is_master(), at which point it is used for the first time in a conditional statement, where "strict" will complain that it's bad to use "eq" when one value is undefined.
    So the question is, what calls "do_command()", and how will you figure out why it's providing an incomplete set of data.

    As for the other things within do_command and do_help that seem to be using $nick without any trouble, you should be aware that it's possible, grammatical, and no problem under "use strict", for a hash key to be an undefined value (although if you "use warnings" or include the "-w" flag, you will get run-time warnings about assigning to or referencing a hash element using an "undef" value as the hash key -- e.g. try the following test script, with and without the "-w" flag (or with and without "use warnings;"):

    use strict; my %hash; my $key = undef; $hash{$key} = 3; print "A hash element with value $hash{$key} is stored with key=$key\n +";
    Obviously, if you repeatedly use an undef or empty hash key to store different values into a hash, subsequent values will replace previous ones.

      If the $nick isn't being initialized, you could just get around this by doing a conditional assignment. my $who = $_[0] || 'narf';

      This would result in a non-initialized value to return false. This might not be the ideal solution, however...

Re: Help with strict
by davido (Cardinal) on Oct 28, 2003 at 19:24 UTC
    You didn't show us how you're calling is_master(), but it sounds like you're calling it without a parameter, or with a parameter whos value is 'undef'. For curiosity's sake, put this:

    die "\$_[0] is undefined\n" if not defined $_[0]; ... as the first line of your is_master() sub. I think you'll find that on this particular invocation of the sub, you'll die.

    The next step will be to figure out why you're passing an undefined value as a parameter to is_master().


    Dave


    "If I had my life to live over again, I'd be a plumber." -- Albert Einstein
      Actually I did, I call the is_master sub on the do_help sub wich is called by the do_command sub...

      I'm starting to think that this is either a ppd(ActivePerl's module format) bug or an ActivePerl bug since I've tested it again without any changes and haven't got that error this time, althrough I always get these:

      Use of uninitialized value in exists at C:/Perl/site/lib/Net/IRC/Connection.pm line 519.
      Use of uninitialized value in exists at C:/Perl/site/lib/Net/IRC/Connection.pm line 519.
      Use of uninitialized value in string eq at C:/Perl/site/lib/Net/IRC/Connection.pm line 1941.
      Use of uninitialized value in string eq at C:/Perl/site/lib/Net/IRC/Connection.pm line 1941.


      with some variations on line numbers... I've first thought that my error was generating these, but since I've only got these this time, I think it's a module problem.

      I'll do some tests on Linux and get back to you guys.

      Thanks,

      my ($author_nickname, $author_email) = ("DaWolf","erabbott\@terra.com.br") if ($author_name eq "Er Galvão Abbott");
Re: Help with strict
by TomDLux (Vicar) on Oct 28, 2003 at 19:26 UTC

    If you use the hash %masters, you don't need the exists. Either $master{$nick} has the value 1, which is true, or else it is undefined, which is false. This also has the benefit of no longer having a routine referencee a global variable.

    If you aren't quite ready to delete the is_master subroutine, quite yet, modify the beginning of subroutine to:

    my $who = $_[0]; my $grant = 0; print "Nick is '$who'\n"; print "Called from ", join( ", ", caller()), ,"\n\n";

    That should simplify debugging.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      The point of putting an exists in is that it works independantly of the actual value in the hash. Perhaps in the future, the program would be expanded to add levels of 'mastery' over the bot, which is implemented by putting different values in the %masters datastructure. In that case, exists $masters{$user} would still tell you if $user is a master, even if it's a user at level 0.

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      :(){ :|:&};:

      Note: All code is untested, unless otherwise stated

        Ah, but if you encapsulate the check in is_master, you only have to update that function once if you ever decide to track user levels.

Re: Help with strict
by Roger (Parson) on Oct 29, 2003 at 01:24 UTC
    Just a comment that you can rewrite the @master assignment to improve readability and save keystrokes -
    my @masters = qw/ John Paul George Ringo /;
    You can use a list to store names and still making it efficient for searching elements in the list, without using a hash table -
    use strict; my @masters = qw/ John Paul George Ringo /; sub is_master { my $who = shift; return scalar grep /^$who$/, @masters; } for (qw/ John George James /) { if (is_master($_)) { print "$_ is master\n" } else { print "$_ is not master\n" } }
    And the output is -
    John is master George is master James is not master
      return scalar grep /^$who$/, @masters;

      Why use a regular expression when you are looking for string equality? What would your code do if $who contained something like 'foo^bar', 'foo{2}', or 'foo[bar]' (all of which are legal IRC nicks?) I think the hash idea given by hardburn is better than a grep, but if you wanted to use grep you should probably do it something like this:

      sub is_master { scalar grep $_[0] eq $_, @masters }
      And if you wanted to be sure it behaved exactly like the original sub, always returning either a 0 or a 1:
      sub is_master { scalar grep $_[0] eq $_, @masters and 1 }

      -sauoq
      "My two cents aren't worth a dime.";