CODE
#!/usr/bin/perl -w
# WeatherPrim
# by: Andrew Burton - tuglyraisin@aol.com
#####
#####
# modules
use HTTP::Response;
use LWP::UserAgent;
use Net::POP3;
use strict;
use XML::Parser;
# mail variables
my $id = 'identity';
my $pw = 'password';
my $servername = 'mail.domain.com';
# Second Life variable
my $slXmlGateway = 'http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi';
my @data = <DATA>;
# Weather Bug XML parsing globals
my $xmlp = XML::Parser->new(Handlers => {Start => \&xml_start,
End => \&xml_end,
Char => \&xml_char});
my $thisday = '';
my $checkx = 0;
my $tag = '';
my $wday = '';
my $wdes = '';
my @weathers = ();
# miscellaneous variables
my $checkm = 0;
my $messtext = '';
my %entry = ();
# main loop
while (1)
{
# Creates the connection
my $pop = &make_pop();
# Logs in
# This will just die if the id and pwd are wrong
my $logcheck = $pop->login($id, $pw);
unless ($logcheck) { die "Die could not login.\n"; }
my $msg = $pop->list();
unless ($msg =~ m/0E0/i) {
foreach my $msgid (keys(%$msg)) {
my $msg_sender = $pop->get($msgid);
foreach my $mline (@$msg_sender)
{
if ($checkm == 1)
{
unless ($mline =~ m/^\n/)
{
$messtext .= $mline;
}
}
if ($mline =~ m/^Object-Name: WeatherPrim/i) { $checkm = 1; }
}
my @emlines = split(/\n/, $messtext);
my ($xmlkey, $wxbkey, $zipcode, $theday) = split(/\,/, $emlines[2]);
if ($theday eq 'today')
{
$theday = 'tonight';
}
my $wreport = &getweather($wxbkey, $zipcode, $theday);
if ($wreport ne "no connection")
{
&sendweather($xmlkey, "100", $wreport);
}
else
{
&sendweather($xmlkey, "400", "Error");
}
# Deletes the message
$pop->delete($msgid);
# Resets some global variables;
$wreport = '';
$checkm = 0;
$messtext = '';
$thisday = '';
$checkx = 0;
$tag = '';
$wday = '';
$wdes = '';
# Cleans out @weathers
while($weathers[0])
{
pop(@weathers);
}
}
}
# closes the connection
$pop->quit();
# Waits a minute before posting
sleep(30);
}
#####
sub sendweather
{
my ($slkey, $code, $weather) = @_;
my $envelope = '';
my @dtemp = @data;
# Loads XML-RPC request into memory
foreach my $line (@dtemp)
{
if ($line)
{
print "$line";
$line =~ s/\$slkey/$slkey/;
$line =~ s/\$code/$code/;
$line =~ s/\$weather/$weather/;
$envelope .= $line;
print "$line";
}
}
# Builds the HTTP request
my $slua = LWP::UserAgent->new();
my $request = HTTP::Request->new(POST => $slXmlGateway);
$request->content_type('text/xml; charset=utf-8');
$request->content($envelope);
# Fires off the HTTP Request
my $result = $slua->request($request);
if ($result->is_success)
{
if ($result->content =~ m/Recieved/)
{
print "101: Connection made.\n";
}
else
{
print "\n\n" . $result->content . "\n\n";
}
}
else
{
print "401: Weather Bug not found.\n";
}
}
#####
sub getweather
{
my ($bugcode, $zipcode, $theday) = @_;
my $ua = LWP::UserAgent->new();
my $url = 'http://' . $bugcode . '.api.wxbug.net/getFullForecast.aspx?' .
'acode=' . $bugcode . '&zipcode=' . $zipcode;
my $result = $ua->get($url);
if ($result->is_success)
{
$thisday = $theday;
my $xml = $result->content;
$xmlp->parsestring($xml);
my $retval = join('|', @weathers);
return($retval);
}
else
{
return("no connection");
}
}
# XML Handlers
# Open tag event
sub xml_start
{
my ($expat, $el, %attr) = @_;
$tag = $el;
}
# Character tag event
sub xml_char
{
my ($expat, $el) = @_;
if ($el =~ m/$thisday/i)
{
$checkx = 1;
}
if ($tag eq 'aws:title' && $checkx == 1)
{
$wday = $el;
}
elsif ($tag eq 'aws:short-title' && $checkx == 1)
{
$wdes = $el;
}
}
# Close tag event
sub xml_end
{
my ($expat, $el) = @_;
if ($el eq "aws:forecast" && $checkx == 1)
{
if (($wday) && ($wdes))
{
my $temp = $wday . ',' . $wdes;
push(@weathers, $temp);
}
$checkx = 0;
}
}
# Creates the POP3 connection
sub make_pop
{
my $pop = Net::POP3->new($servername, Default => 1);
unless ($pop)
{
print "Cannot make POP3 connection.\n";
sleep(5);
$pop = &make_pop();
}
return($pop);
}
__DATA__
<?xml version="1.0"?>
<methodCall>
<methodName>llRemoteData</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>Channel</name>
<value><string>$slkey</string></value>
</member>
<member>
<name>IntValue</name>
<value><int>$code</int></value>
</member>
<member>
<name>StringValue</name>
<value><string>$weather</string></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
LSL code
CODE
// Global variables
key gUser;
key gChannel;
integer gListen;
integer qCount = 0;
integer tCount = 1;
integer isReady = 0;
string gZip;
string gWxBugKey;
string gDay;
string weatherMail = "identity@domain.com";
// To read notecard data
string noteName = "wxbug";
integer noteLine = 0;
key noteQuery;
// Stole this from the Wiki, it dumps error info
DEBUG(list out)
{
llSay(0, llList2CSV(out));
}
/////
// This is the default state
default
{
state_entry()
{
if (gWxBugKey == "")
{
noteQuery = llGetNotecardLine(noteName, noteLine);
}
llSetColor(<0, 255, 0>, ALL_SIDES);
if (isReady == 0)
{
llSay(0, "WeatherPrim ready.");
isReady = 1;
}
}
dataserver(key query_id, string data)
{
if (query_id == noteQuery)
{
if (data != EOF)
{
gWxBugKey = data;
}
}
}
touch(integer total_number)
{
llSetColor(<255, 0, 0>, ALL_SIDES);
gUser = llDetectedKey(0);
state cmdline;
}
}
/////
// This is the state that handles user interfacing
state cmdline
{
state_entry()
{
llSay(0, "In which U.S. zip code do you want to know about the weather?");
llSay(0, "Please just give say the first five digits.");
gListen = llListen(0, "", gUser, "");
}
listen(integer channel, string name, key id, string message)
{
if (qCount == 1)
{
message = llToLower(message);
if (message == "monday" || message == "tuesday" || message == "wednesday" ||
message == "thursday" || message == "friday" || message == "saturday" ||
message == "sunday")
{
gDay = message;
llSay(0, "Okay, let me look that up.");
llOpenRemoteDataChannel();
}
else if (message == "today")
{
gDay = message;
llSay(0, "Today's weather? Why not just look out a window?");
llSleep(2);
llSay(0, "Just kidding.");
llOpenRemoteDataChannel();
}
else
{
llSay(0, "Could you repeat that, I misunderstood you.");
}
}
else if (qCount == 0)
{
if (llStringLength(message) == 5)
{
qCount++;
gZip = message;
llSay(0, "And what day do you want to know about?");
llSay(0, "Please just tell me either the weekday, or say today.");
}
else
{
llSay(0, "Sorry, what was that zip code?");
}
}
}
remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
{
if (type == REMOTE_DATA_CHANNEL)
{
gChannel = channel;
state xmlrpc;
}
else
{
DEBUG(["Unexpected event type", type, channel, message_id, sender, ival, sval]);
}
}
state_exit()
{
llSay(0, "This could take a minute, please be patient.");
}
}
/////
// This is the state that accepts data from the net
state xmlrpc
{
state_entry()
{
llSetTimerEvent(1.0);
string weathermsg = (string)gChannel + "," + gWxBugKey + "," + gZip + "," + gDay;
llEmail(weatherMail, "weather request", weathermsg);
}
remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
{
if (type == REMOTE_DATA_REQUEST)
{
if (ival == 100)
{
llRemoteDataReply(channel, message_id, "Recieved", 101);
list dayList = llParseString2List(sval, ["|"], []);
integer dlcnt = 0;
while (llList2String(dayList, dlcnt) != "")
{
list dayInfo = llParseString2List(llList2String(dayList, dlcnt), [","], []);
string day = llList2String(dayInfo, 0);
string cnd = llList2String(dayInfo, 1);
llSay(0, cnd + " on " + day + ".");
dlcnt++;
}
state default;
}
else if (ival == 400)
{
llRemoteDataReply(channel, message_id, "Recieved", 401);
llSay(0, "Looks like there was a problem. Pleast try again later.");
state default;
}
else
{
llSay(0, sval);
llRemoteDataReply(channel, message_id, "Sorry, I have no response for that code.", 0);
}
}
else
{
DEBUG(["Unexpected event type:", type, channel, message_id, sender, ival, sval]);
}
}
timer()
{
if (tCount < 120)
{
tCount++;
}
else
{
llSay(0, "I do apologize. There seems to be an error. Please try again later.");
llSetTimerEvent(0.0);
state default;
}
}
state_exit()
{
llCloseRemoteDataChannel(gChannel);
llListenRemove(gListen);
gZip = "";
gDay = "";
tCount = 0;
qCount = 0;
llSleep(2);
}
}
Installation Instructions:
1. Load weather.pl on your server.
2. Change $id, $pw, and $servername to match your information.
3. Create a notecard called wxbug, and place your WeatherBug API licensecode it.
4. Create a prim, and place the LSL code into it.
5. Change weatherMail to your email address and save the code.
6. Click on your WeatherPrim, and give it a whirl.
Known Issues:
Once in a while, weather.pl will crash because of a problem with XML::Parser about 10-15% of the time. I'm still trying to figure out why it does this, but it is a known issue.