pjs/webtools/mozbot/BotModules/RDF.bm

256 строки
9.4 KiB
Plaintext

################################
# RDF Module #
################################
package BotModules::RDF;
use vars qw(@ISA);
@ISA = qw(BotModules);
1;
# RegisterConfig - Called when initialised, should call registerVariables
sub RegisterConfig {
my $self = shift;
$self->SUPER::RegisterConfig(@_);
$self->registerVariables(
# [ name, save?, settable? ]
['sites', 1, 1, {}],
['updateDelay', 1, 1, 600],
['preferredLineLength', 1, 1, 80],
['maxInChannel', 1, 1, 5],
['trimTitles', 1, 1, '0'],
['data', 0, 0, {}], # data -> uri -> (title, link, last, items -> uri)
['mutes', 1, 1, {}], # uri -> "channel channel channel"
);
}
# Schedule - called when bot connects to a server, to install any schedulers
# use $self->schedule($event, $delay, $times, $data)
# where $times is 1 for a single event, -1 for recurring events,
# and a +ve number for an event that occurs that many times.
sub Schedule {
my $self = shift;
my ($event) = @_;
$self->schedule($event, \$self->{'updateDelay'}, -1, 'rdf');
$self->SUPER::Schedule($event);
}
sub Help {
my $self = shift;
my ($event) = @_;
my %commands;
if ($self->isAdmin($event)) {
$commands{''} = "The RDF module monitors various websites. Add new RDF channels to the 'sites' hash. Duplicates with different nicknames are fine. For example, \"vars $self->{'_name'} sites '+|slashdot|http://...'\" and \"vars $self->{'_name'} sites '+|/.|http://...'\" is fine.";
$commands{'mute'} = 'Disable reporting of a site in a channel. (Only does something if the given site exists.) Syntax: mute <site> in <channel>';
$commands{'unmute'} = 'Enable reporting of a site in a channel. By default, sites are reported in all channels that the module is active in. Syntax: unmute <site> in <channel>';
} else {
$commands{''} = 'The RDF module monitors various websites.';
}
foreach my $site (keys(%{$self->{'sites'}})) {
if ($self->{'data'}->{$self->{'sites'}->{$site}}) {
$commands{$site} = "Reports the headlines listed in $self->{'data'}->{$self->{'sites'}->{$site}}->{'title'}";
# -- #mozilla was here --
# <Hixie> anyway, $self->{'data'}->{$self->{'sites'}->{$site}}->{'title'} is
# another nice piece of perl (embedded in a quoted string in this case)
# <moogle> yeah, that's a bit more familiar
# <jag> Oooh, nice one
# <jag> Reminds me of Java, a bit :-)
# <jag> Without all the casting about from Object to Hashtable
# <Hixie> all this, BTW, is from the RDF module (the one that mozbot uses to
# report changes in mozillazine and so on)
# <moogle> I still tend to comment these things a bit just for maintainability
# by others who might not wish to do mental gymnastics :)
# <Hixie> :-)
} else {
$commands{$site} = "Reports the headlines listed in $self->{'sites'}->{$site}";
}
}
return \%commands;
}
sub Told {
my $self = shift;
my ($event, $message) = @_;
foreach my $site (keys(%{$self->{'sites'}})) {
if ($message =~ /^\s*(\Q$site\E)\s*$/si) {
$self->GetSite($event, $1, 'request');
return 0; # dealt with it...
}
}
if ($self->isAdmin($event)) {
if ($message =~ /^\s*mute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
my $site = $1 eq 'RDF' ? '' : $self->{'sites'}->{$1};
my $siteName = $site eq '' ? 'all sites' : $site;
if (defined($site)) {
$self->{'mutes'}->{$site} .= " $2";
$self->saveConfig();
$self->say($event, "$event->{'from'}: RDF notifications for $siteName muted in channel $2.");
} else {
# can't say this, other modules might recognise it: $self->say($event, "$event->{'from'}: I don't know about any '$1' site...");
}
} elsif ($message =~ /^\s*unmute\s+(\S+?)\s+in\s+(\S+?)\s*$/osi) {
my $site = $1 eq 'RDF' ? '' : $self->{'sites'}->{$1};
my $siteName = $site eq '' ? 'all sites' : $site;
if (defined($site)) {
my %mutedChannels = map { lc($_) => 1 } split(/ /o, $self->{'mutes'}->{$site});
delete($mutedChannels{lc($2)}); # get rid of any mentions of that channel
$self->{'mutes'}->{$site} = join(' ', keys(%mutedChannels));
$self->saveConfig();
$self->say($event, "$event->{'from'}: RDF notifications for $siteName resumed in channel $2.");
} else {
# can't say this, other modules might recognise it: $self->say($event, "$event->{'from'}: I don't know about any '$1' site...");
}
} else {
return $self->SUPER::Told(@_);
}
} else {
return $self->SUPER::Told(@_);
}
return 0;
}
sub GetSite {
my $self = shift;
my ($event, $site, $intent) = @_;
if (defined($self->{'sites'}->{$site})) {
my $uri = $self->{'sites'}->{$site};
$self->getURI($event, $uri, $intent);
} else {
# XXX
}
}
sub GotURI {
my $self = shift;
my ($event, $uri, $output, $intent) = @_;
$self->{'data'}->{$uri}->{'ready'} = defined($self->{'data'}->{$uri});
if ($output) {
# last update stamp
$self->{'data'}->{$uri}->{'last'} = time();
# this, of course, is a disaster waiting to happen.
# for example, we don't cope with comments.
# someone write a real XML version of this pleeeeease... XXX
# get the juicy stuff out
my $channelpart = "";
if ($output =~ /<channel>(.*)<\/channel>/osi) {
$channelpart = $1;
}
# remove any image related stuff
$output =~ s/<image>.*<\/image>//gosi;
# get the channel title
$self->{'data'}->{$uri}->{'title'} = $uri;
if ($channelpart =~ /<title>\s*(.+?)\s*<\/title>/osi) {
$self->{'data'}->{$uri}->{'title'} = $self->unescapeXML($1);
$self->{'data'}->{$uri}->{'title'} =~ s/: News for nerds, stuff that matters//gosi if $self->{'trimTitles'};
}
# get the channel website
$self->{'data'}->{$uri}->{'link'} = $uri;
if ($channelpart =~ /<link>\s*(.+?)\s*<\/link>/osi) {
$self->{'data'}->{$uri}->{'link'} = $self->unescapeXML($1);
}
# get all the items
while ($output =~ /<item>.*?<title>\s*(.+?)\s*<\/title>.*?<\/item>/osig) {
unless (($1 =~ /^last update/osi) or (defined($self->{'data'}->{$uri}->{'items'}->{$self->unescapeXML($1)}))) {
$self->{'data'}->{$uri}->{'items'}->{$self->unescapeXML($1)} = $self->{'data'}->{$uri}->{'last'};
}
}
$self->ReportDiffs($event, $uri, $intent);
if ($intent eq 'request') {
$self->ReportAll($event, $uri);
}
} else {
if ($intent eq 'request') {
$self->say($event, "$event->{'from'}: Dude, the file was empty! ($uri)");
}
}
}
sub Scheduled {
my $self = shift;
my ($event, @data) = @_;
if ($data[0] eq 'rdf') {
my %sites = map { $_ => 1 } values(%{$self->{'sites'}});
foreach (keys(%sites)) {
$self->getURI($event, $_, 'update');
}
} else {
$self->SUPER::Scheduled($event, @data);
}
}
sub ReportDiffs {
my $self = shift;
my ($event, $uri, $request) = @_;
return unless $self->{'data'}->{$uri}->{'ready'};
my $last = $self->{'data'}->{$uri}->{'last'};
my @output;
foreach (keys(%{$self->{'data'}->{$uri}->{'items'}})) {
push(@output, $_) if ($self->{'data'}->{$uri}->{'items'}->{$_} == $last);
}
if (@output) {
@output = $self->prettyPrint($self->{'preferredLineLength'},
"Just appeared in $self->{'data'}->{$uri}->{'title'} - $self->{'data'}->{$uri}->{'link'} : ",
'', ' -- ', @output);
my %mutedChannels = ();
if (defined($self->{'mutes'}->{$uri})) {
%mutedChannels = map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{$uri});
}
if (defined($self->{'mutes'}->{''})) {
%mutedChannels = (%mutedChannels, map { lc($_) => 1 } split(/\s+/os, $self->{'mutes'}->{''}));
}
if ($request eq 'request') {
$mutedChannels{$event->{'channel'}} = 1;
}
foreach (@{$self->{'channels'}}) {
unless ($mutedChannels{$_}) {
local $event->{'target'} = $_;
foreach (@output) {
$self->say($event, $_);
}
}
}
}
}
sub ReportAll {
my $self = shift;
my ($event, $uri) = @_;
my @output;
foreach (keys(%{$self->{'data'}->{$uri}->{'items'}})) {
push(@output, $_);
}
@output = $self->prettyPrint($self->{'preferredLineLength'},
"Items in $self->{'data'}->{$uri}->{'title'} - $self->{'data'}->{$uri}->{'link'}: ",
"$event->{'from'}: ", ' -- ', @output);
if (@output > $self->{'maxInChannel'}) {
foreach (@output) {
$self->directSay($event, $_);
}
$self->channelSay($event, "$event->{'from'}: /msg'ed");
} else {
foreach (@output) {
$self->say($event, $_);
}
}
}