Newbie Perl programming help (RSS & IRC)

C

Chris

I am new to perl. and have got stuck on a error that occurs in my
program. The error that i get is the following

~/test]# perl girbot.pl
Global symbol "$latest" requires explicit package name at girbot.pl
line 148.
Global symbol "$rss" requires explicit package name at girbot.pl line
151.
Global symbol "$latest" requires explicit package name at girbot.pl
line 159.
Execution of girbot.pl aborted due to compilation errors.


My program looks like this. The bit that i am having problems with is
the bit that generates the rss file. Also i am not exactly sure if my
regex are corrent. In theory they should search for ed2k:// in $arg and
if it is found, save $arg to the rss file. When I get it working i will
use substr to just pull out the ed2k:// link. But first i have to get
around those nast errors that are above.

can anybody help?

thanks
chris


#!/usr/bin/perl

# Usage: perl girbot.pl <config file, optional>
# <setting>=<value>

use strict;
use Net::IRC;
use XML::RSS;



sub GetSetting
{
my ($setting,$config_file)=@_;
open(CFGFILE,"<$config_file")
or die "Can't open configuration file ($config_file)";
my @slist=<CFGFILE>;
foreach my $selem (@slist)
{
if (index($selem,"#")==0) { next; }
my @ln=split("=",$selem);
if ($ln[0] =~ /$setting/i)
{
chomp $ln[1];
return $ln[1];
}
}
close CFGFILE;
}

# =============
# MAIN BOT CODE
# =============

# Set our configuration file
my $configuration_file = "gir.cfg";
# You can start the bot with a config file as a
# commandline argument. Without the argument,
# the bot loads its settings from "gir.cfg", in
# the same directory as the bot.
if($#ARGV==0) { $configuration_file=$ARGV[0]; }
# Now, we can load in our script's settings
my $cfg_nick=GetSetting("nick",$configuration_file);
my $cfg_altnick=GetSetting("altnick",$configuration_file);
my $cfg_ident=GetSetting("ident",$configuration_file);
my $cfg_port=GetSetting("port",$configuration_file);
my $cfg_server=GetSetting("server",$configuration_file);
my $cfg_channel=GetSetting("channel", $configuration_file);
# Just about all of the settings are "strings", except
# for the "port". Let's make sure that that setting
# is numerical, and if not, set it to the most common
# port, 6667:
if($cfg_port=~/\D/) { $cfg_port=6667; }

# Now that all of our settings are loaded in,
# let's create the IRC object
my $irc = new Net::IRC;
print "Creating connection to IRC server...";
my $conn = $irc->newconn(Server => "$cfg_server",
Port => $cfg_port,
Nick => "$cfg_nick",
Ircname => "$cfg_ident",
Username => "$cfg_ident")
or die "Can't connect to IRC server.";
print "done!\n";
$conn->{channel} = '#sonic';
# With that out of the way, let's create
# some subs for our object handlers

# What our bot will do when it finishes
# connecting to the IRC server
sub on_connect {
my $self = shift;
print "*** Connected to IRC.\n";
$conn->join("$cfg_channel");
print "*** Joined Channel: $cfg_channel.\n";
}
# This sub will print various
# incoming date while we're still
# connecting to IRC
sub on_init {
my ($self, $event) = @_;
my (@args) = ($event->args);
shift (@args);

print "*** @args\n";
}
# This sub will handle what happens when the
# bot receives public (channel) text.
sub on_public {
my ($self, $event) = @_;
my @to = $event->to;
my ($nick, $mynick) = ($event->nick, $self->nick); # Sender text,
Bot nick
my $host=$event->host; # Sender's hostname
my ($arg) = ($event->args); # The message

# Here's where we want to "parse" channel text
print "<$nick> $arg\n";

if ($nick eq "eZebra" && $arg=~m/ed2k:\/\//i)
{
if ($latest == 1)
{
#&add_ed2k();
$rss->add_item(
link => '$arg'
);
}
}

if ($nick eq "eZebra" && $arg=~m/--- Latest links: ---/i)
{
if ($latest != 1)
{
my $latest = 1;
#&start_rss();
my $rss = new XML::RSS(version => '0.91');
$rss->channel(
title => 'Test RSS Feed',
link => 'irc://irc.efnet.net/test',
description => 'Toast'
);
}
}

if ($nick eq "eZebra" && $arg=~m/------/i)
{
#&end_rss();
my $latest = 0;
rss->save("~/feed.rss");
}


}
# This sub will handle what happens when the
# bot receives private message text
sub on_msg {
my ($self, $event) = @_;
my ($nick) = $event->nick; # Message Sender
my ($arg) = ($event->args); # Message Text
my $host=$event->host;

# Here's where we want to "parse" message text
print " - $nick - $arg\n";

}
# This sub will get triggered if our bot's nick
# is taken, setting it to our alternate nick.
sub on_nick_taken {
my ($self) = shift;

$self->nick($cfg_altnick);
}

sub on_join {

# get our connection object and the event object, which is passed
# with this event automatically
my ($conn, $event) = @_;

# this is the nick that just joined
my $nick = $event->{nick};
# say hello to the nick in public
$conn->privmsg($conn->{channel}, "Hello, $nick!");
if ($nick = "testuser")
{
$conn->mode('#sonic', '+o', 'testuser');
print "*** Modifying $nick Mode to: +o\n";
}
}

# Now that all of our handler subs are created,
# let's install them
print "Installing local handlers...";
$conn->add_handler('public', \&on_public);
$conn->add_handler('msg', \&on_msg);
$conn->add_handler('join', \&on_join);
print "done!\nInstalling global handlers...";
$conn->add_global_handler([ 251,252,253,254,302,255 ], \&on_init);
$conn->add_global_handler([376, 422], \&on_connect);
$conn->add_global_handler(433, \&on_nick_taken);

print "done!\n";
# Everything's installed, so there's nothing
# holding up back from starting up!
$irc->start;
 
C

Chris Mattern

Chris said:
I am new to perl. and have got stuck on a error that occurs in my
program. The error that i get is the following

~/test]# perl girbot.pl
Global symbol "$latest" requires explicit package name at girbot.pl
line 148.
Global symbol "$rss" requires explicit package name at girbot.pl line
151.
Global symbol "$latest" requires explicit package name at girbot.pl
line 159.
Execution of girbot.pl aborted due to compilation errors.
A cursory look at your code reveals that you attempt to use $rss and
$latest before you declare them with "my". Can't do that. Also remember
that if you declare a variable with my inside a {} block, the variable
does not exist anywhere *outside* of that block. Oh, and use warnings.

--
Christopher Mattern

"Which one you figure tracked us?"
"The ugly one, sir."
"...Could you be more specific?"
 
S

Sherm Pendley

Chris said:
I am new to perl. and have got stuck on a error that occurs in my
program. The error that i get is the following

Global symbol "$latest" requires explicit package name at girbot.pl
line 148.

.... snip ...
if ($nick eq "eZebra" && $arg=~m/ed2k:\/\//i)
{
if ($latest == 1)

There's no variable named $latest in scope here

{
#&add_ed2k();

Why do you want to disable prototypes when you call add_ed2k()? (If you
don't understand what prototypes are, or why I asked that question, have a
look at "perldoc perlsub".)
Global symbol "$rss" requires explicit package name at girbot.pl line
151.
$rss->add_item(

There's no variable named $rss in scope here
link => '$arg'

$arg will not interpolate here, because you've used single quotes. (If
you're thinking of using double quotes instead, have a look at "perldoc -q
always".)
Global symbol "$latest" requires explicit package name at girbot.pl
line 159.
if ($latest != 1)

Same thing here - there's no $latest variable in scope here. Also...
{
my $latest = 1;

If there *were* a $latest variable already, this declaration would mask it
by creating one that's visible only within this block.
#&start_rss();
my $rss = new XML::RSS(version => '0.91');
$rss->channel(
title => 'Test RSS Feed',
link => 'irc://irc.efnet.net/test',
description => 'Toast'
);
}
}

if ($nick eq "eZebra" && $arg=~m/------/i)
{
#&end_rss();
my $latest = 0;
rss->save("~/feed.rss");
}

You have the same problem(s) here - the $latest you declare here will mask
any $latest that happens to exist in the enclosing block. And, because
you've declared $rss in a smaller block than the one where it's used, it
won't be visible here.

Variables need to be declared in the smallest scope that allows them to be
visible wherever they're needed - but no smaller. For example, let's
suppose you have a variable $foo, that needs to be visible from within an
entire sub, which has two loops. You'd need to declare it inside the sub,
but *outside* of the two loops, like this:

sub do_something {
my $foo;
foreach (@bar) {
# do something with $foo...
}
foreach (@baz) {
# do something else with $foo
}
}

You only need to declare each variable once. If you think you need to
re-declare a variable because you're getting "Global symbol requires
package" errors like the above, it could be because the variable has gone
out of scope. For example:

sub do_something {
foreach (@bar) {
my $foo = 'bleh';
}
foreach (@baz) {
my $oops = $foo; # <--- This will cause an error, because
# the $foo created above was valid only
# within that block, and no longer exists
# in this one
}
}

Simply adding a "my $foo" to declare a new $foo variable inside the second
loop is not the correct solution in this instance. Instead, you need to
move the "my $foo" declaration up to a higher-level block, so that it's
visible to all the code that uses it, like in the first example.

sherm--
 
C

Chris

Thanks for the help. It is much appreciated. A couple more things.

So would declaring the variables with the "local" command to make them
global variables work? As what I am doing in this program is monitoring
the the output of an irc channel. On every public message that is sent
from the nick "eZebra", that contains an ed2k link to write it to an
rss file. So what I think I need is the $rss and the $latest variable
to be global to the whole program. So that I can read the contents
wherever I am.

I do find programming Net::IRC a little odd. I am not used to event
driven perl + I am new to perl anyway.

thanks
Chris
 
S

Sherm Pendley

Chris said:
So would declaring the variables with the "local" command to make them
global variables work?

"local" doesn't do what most new Perl programmers expect it to do. Read up
on it - "perldoc -q scoping", "perldoc -f local".
the the output of an irc channel. On every public message that is sent
from the nick "eZebra", that contains an ed2k link to write it to an
rss file. So what I think I need is the $rss and the $latest variable
to be global to the whole program.

So declare them that way. When you use my() inside a subroutine or other
block, you're declaring a new lexical variable that exists only inside that
block.

Instead of doing that, declare a variable at the top of the file with my(),
so it's visible to all the code in that file. Or go a step further and use
our(), so that it's visible to any code within the same package.
I do find programming Net::IRC a little odd. I am not used to event
driven perl + I am new to perl anyway.

This has nothing at all to do with your app being event-driven.

Although, it *is* important to understand early on. I think you should take
a step back from the app you're working on, go to <http://learn.perl.org>
and study variable scoping until you're comfortable with it.

Several sections of "perldoc perlsub" discuss scoping as well.

sherm--
 
C

Chris

Thanks for your help. I have now progressed more. However I have hit
another problem. I have worked out where I need to define the $rss and
$enable variables and that in each sub() that I need to do something
like a my ($rss, $enable) = @_; to allow the sub() access to the
variables. The problem that I think I have now is that it seems that
the data that is in $rss & $enable is not passed back out of the
sub's() when the sub() ends. So when the program comes to want to save
the data, it can't because the variable's are blank.

Do you understand what I mean?

The error that I get when the rss saves is

Can't call method "save" on an undefined value at ./sharetvrss.pl line
171, <CFGFILE> line 36.

Below is the latest version of the program.

I think that by the time I get this program fixed, I will definatly
understand how variables are passed around.

thanks
chris



#!/usr/bin/perl

use strict;
use Net::IRC;
use XML::RSS;


sub GetSetting
{
my ($setting,$config_file)=@_;
open(CFGFILE,"<$config_file")
or die "Can't open configuration file ($config_file)";
my @slist=<CFGFILE>;
foreach my $selem (@slist)
{
if (index($selem,"#")==0) { next; }
my @ln=split("=",$selem);
if ($ln[0] =~ /$setting/i)
{
chomp $ln[1];
return $ln[1];
}
}
close CFGFILE;
}

# =============
# MAIN BOT CODE
# =============

# Set our configuration file
my $configuration_file = "sharetvrss.cfg";
# You can start the bot with a config file as a
# commandline argument. Without the argument,
# the bot loads its settings from "gir.cfg", in
# the same directory as the bot.
if($#ARGV==0) { $configuration_file=$ARGV[0]; }
# Now, we can load in our script's settings
my $cfg_nick=GetSetting("nick",$configuration_file);
my $cfg_altnick=GetSetting("altnick",$configuration_file);
my $cfg_ident=GetSetting("ident",$configuration_file);
my $cfg_port=GetSetting("port",$configuration_file);
my $cfg_server=GetSetting("server",$configuration_file);
my $cfg_channel=GetSetting("channel", $configuration_file);
my $rss = new XML::RSS(version => '0.91');
my $enable = 1;
# Just about all of the settings are "strings", except
# for the "port". Let's make sure that that setting
# is numerical, and if not, set it to the most common
# port, 6667:
if($cfg_port=~/\D/) { $cfg_port=6667; }

# Now that all of our settings are loaded in,
# let's create the IRC object
my $irc = new Net::IRC;
print "Creating connection to IRC server...";
my $conn = $irc->newconn(Server => "$cfg_server",
Port => $cfg_port,
Nick => "$cfg_nick",
Ircname => "$cfg_ident",
Username => "$cfg_ident")
or die "Can't connect to IRC server.";
print "done!\n";
$conn->{channel} = '#sonic';
# With that out of the way, let's create
# some subs for our object handlers

# What our bot will do when it finishes
# connecting to the IRC server
sub on_connect {
my $self = shift;
print "*** Connected to IRC.\n";
$conn->join("$cfg_channel");
print "*** Joined Channel: $cfg_channel.\n";
}
# This sub will print various
# incoming date while we're still
# connecting to IRC
sub on_init {
my ($self, $event) = @_;
my (@args) = ($event->args);
shift (@args);

print "*** @args\n";
}
# This sub will handle what happens when the
# bot receives public (channel) text.
sub on_public {
my ($self, $event, $rss, $enable) = @_;
my @to = $event->to;
my ($nick, $mynick) = ($event->nick, $self->nick); # Sender text,
Bot nick
my $host=$event->host; # Sender's hostname
my ($arg) = ($event->args); # The message

# Here's where we want to "parse" channel text
print "<$nick> $arg\n";

if ($nick eq "eZebra" && $arg=~/ed2k:\/\// && $enable==1)
{
#&add_ed2k();
$rss->add_item(
link => "$arg"
);
}

if ($nick eq "eZebra" && $arg=~m/--- Latest links: ---/i)
{
#&start_rss();
$enable=1;
$rss = new XML::RSS(version => '0.91');
$rss->channel(
title => 'RSS Feed',
link => 'irc://irc.efnet.net/test',
description => 'Toast'
);
}

if ($nick eq "eZebra" && $arg=~m/------/i)
{
#&end_rss();
$rss->save("/home/cmw/public_html/test/testss.rss");
$enable=0;
}


}
# This sub will handle what happens when the
# bot receives private message text
sub on_msg {
my ($self, $event) = @_;
my ($nick) = $event->nick; # Message Sender
my ($arg) = ($event->args); # Message Text
my $host=$event->host;

# Here's where we want to "parse" message text
print " - $nick - $arg\n";

}
# This sub will get triggered if our bot's nick
# is taken, setting it to our alternate nick.
sub on_nick_taken {
my ($self) = shift;

$self->nick($cfg_altnick);
}

sub on_join {

# get our connection object and the event object, which is
passed
# with this event automatically
my ($conn, $event) = @_;

# this is the nick that just joined
my $nick = $event->{nick};
# say hello to the nick in public
#$conn->privmsg($conn->{channel}, "Hello, $nick!");
if ($nick = "mynick")
{
$conn->mode('#sonic', '+o', 'mynick');
print "*** Modifying $nick Mode to: +o\n";
}
}

# Now that all of our handler subs are created,
# let's install them
print "Installing local handlers...";
$conn->add_handler('public', \&on_public);
$conn->add_handler('msg', \&on_msg);
$conn->add_handler('join', \&on_join);
print "done!\nInstalling global handlers...";
$conn->add_global_handler([ 251,252,253,254,302,255 ], \&on_init);
$conn->add_global_handler([376, 422], \&on_connect);
$conn->add_global_handler(433, \&on_nick_taken);

print "done!\n";
# Everything's installed, so there's nothing
# holding up back from starting up!
$irc->start;
 
C

Chris

Thanks for your help. I have now progressed more. However I have hit
another problem. I have worked out where I need to define the $rss and
$enable variables and that in each sub() that I need to do something
like a my ($rss, $enable) = @_; to allow the sub() access to the
variables. The problem that I think I have now is that it seems that
the data that is in $rss & $enable is not passed back out of the
sub's() when the sub() ends. So when the program comes to want to save
the data to rss, it can't because it thinks the variables are empty and
therefore crashes the program with the below error.

Do you understand what I mean?

The error that I get when the rss saves is

Can't call method "save" on an undefined value at ./sharetvrss.pl line
171, <CFGFILE> line 36.

Below is the latest version of the program.

thanks
chris



#!/usr/bin/perl

use strict;
use Net::IRC;
use XML::RSS;


sub GetSetting
{
my ($setting,$config_file)=@_;
open(CFGFILE,"<$config_file")
or die "Can't open configuration file ($config_file)";
my @slist=<CFGFILE>;
foreach my $selem (@slist)
{
if (index($selem,"#")==0) { next; }
my @ln=split("=",$selem);
if ($ln[0] =~ /$setting/i)
{
chomp $ln[1];
return $ln[1];
}
}
close CFGFILE;
}

# =============
# MAIN BOT CODE
# =============

# Set our configuration file
my $configuration_file = "sharetvrss.cfg";
# You can start the bot with a config file as a
# commandline argument. Without the argument,
# the bot loads its settings from "gir.cfg", in
# the same directory as the bot.
if($#ARGV==0) { $configuration_file=$ARGV[0]; }
# Now, we can load in our script's settings
my $cfg_nick=GetSetting("nick",$configuration_file);
my $cfg_altnick=GetSetting("altnick",$configuration_file);
my $cfg_ident=GetSetting("ident",$configuration_file);
my $cfg_port=GetSetting("port",$configuration_file);
my $cfg_server=GetSetting("server",$configuration_file);
my $cfg_channel=GetSetting("channel", $configuration_file);
my $rss = new XML::RSS(version => '0.91');
my $enable = 1;
# Just about all of the settings are "strings", except
# for the "port". Let's make sure that that setting
# is numerical, and if not, set it to the most common
# port, 6667:
if($cfg_port=~/\D/) { $cfg_port=6667; }

# Now that all of our settings are loaded in,
# let's create the IRC object
my $irc = new Net::IRC;
print "Creating connection to IRC server...";
my $conn = $irc->newconn(Server => "$cfg_server",
Port => $cfg_port,
Nick => "$cfg_nick",
Ircname => "$cfg_ident",
Username => "$cfg_ident")
or die "Can't connect to IRC server.";
print "done!\n";
$conn->{channel} = '#sonic';
# With that out of the way, let's create
# some subs for our object handlers

# What our bot will do when it finishes
# connecting to the IRC server
sub on_connect {
my $self = shift;
print "*** Connected to IRC.\n";
$conn->join("$cfg_channel");
print "*** Joined Channel: $cfg_channel.\n";
}
# This sub will print various
# incoming date while we're still
# connecting to IRC
sub on_init {
my ($self, $event) = @_;
my (@args) = ($event->args);
shift (@args);

print "*** @args\n";
}
# This sub will handle what happens when the
# bot receives public (channel) text.
sub on_public {
my ($self, $event, $rss, $enable) = @_;
my @to = $event->to;
my ($nick, $mynick) = ($event->nick, $self->nick); # Sender text,
Bot nick
my $host=$event->host; # Sender's hostname
my ($arg) = ($event->args); # The message

# Here's where we want to "parse" channel text
print "<$nick> $arg\n";

if ($nick eq "eZebra" && $arg=~/ed2k:\/\// && $enable==1)
{
#&add_ed2k();
$rss->add_item(
link => "$arg"
);
}

if ($nick eq "eZebra" && $arg=~m/--- Latest links: ---/i)
{
#&start_rss();
$enable=1;
$rss = new XML::RSS(version => '0.91');
$rss->channel(
title => 'RSS Feed',
link => 'irc://irc.efnet.net/test',
description => 'Toast'
);
}

if ($nick eq "eZebra" && $arg=~m/------/i)
{
#&end_rss();
$rss->save("/home/cmw/public_html/test/testss.rss");
$enable=0;
}


}
# This sub will handle what happens when the
# bot receives private message text
sub on_msg {
my ($self, $event) = @_;
my ($nick) = $event->nick; # Message Sender
my ($arg) = ($event->args); # Message Text
my $host=$event->host;

# Here's where we want to "parse" message text
print " - $nick - $arg\n";

}
# This sub will get triggered if our bot's nick
# is taken, setting it to our alternate nick.
sub on_nick_taken {
my ($self) = shift;

$self->nick($cfg_altnick);
}

sub on_join {

# get our connection object and the event object, which is
passed
# with this event automatically
my ($conn, $event) = @_;

# this is the nick that just joined
my $nick = $event->{nick};
# say hello to the nick in public
#$conn->privmsg($conn->{channel}, "Hello, $nick!");
if ($nick = "mynick")
{
$conn->mode('#sonic', '+o', 'mynick');
print "*** Modifying $nick Mode to: +o\n";
}
}

# Now that all of our handler subs are created,
# let's install them
print "Installing local handlers...";
$conn->add_handler('public', \&on_public);
$conn->add_handler('msg', \&on_msg);
$conn->add_handler('join', \&on_join);
print "done!\nInstalling global handlers...";
$conn->add_global_handler([ 251,252,253,254,302,255 ], \&on_init);
$conn->add_global_handler([376, 422], \&on_connect);
$conn->add_global_handler(433, \&on_nick_taken);

print "done!\n";
# Everything's installed, so there's nothing
# holding up back from starting up!
$irc->start;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top