gecko-dev/webtools/mozbot/BotModules/RDF.bm

265 строки
9.9 KiB
Plaintext

################################
# RDF Module #
################################
# this is really an RSS module, not an RDF module.
# but oh well.
package BotModules::RDF;
use XML::RSS;
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. To remove a site from the RDF 'sites' hash, use this syntax \"vars $self->{_name} sites '-slashdot'";
$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
my $last = $event->{'time'};
$self->{'data'}->{$uri}->{'last'} = $last;
# Parse It
my $rss = XML::RSS->new();
eval { $rss->parse($output) };
if ($@) {
$self->debug("$uri is not a valid RSS file");
if ($intent eq 'request') {
$self->say($event, "$event->{'from'}: Dude, the file is not valid RSS! ($uri)");
}
return;
}
# Set Link and Title
$self->{data}->{$uri}->{'link'} = $rss->{'channel'}->{'link'};
$self->{data}->{$uri}->{'title'} = $rss->{'channel'}->{'title'};
foreach my $item (@{$rss->{'items'}}) {
unless (($item->{title} =~ /^last update/osi) ||
(defined($self->{'data'}->{$uri}->{'items'}->{$item->{'title'}}))) {
$self->{'data'}->{$uri}->{'items'}->{$item->{'title'}} = $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);
}
# -- #mrt was here --
# <mozbot> Friday's security advisories -- The first stable
# Xen release -- Linux Gazette #95
# <mozbot> KDE Under The Microscope -- Additional OpenSSL info
# <Hixie> wtf
# <mozbot> Just appeared in jbisbee.com -
# http://www.jbisbee.com/ : PoCo::RSS::Aggregator
# <Hixie> why is it repeating the same thing over and over
# <mozbot> PoCo::RSSAggregator & XML::RSS::Feed Uploaded to
# CPAN -- More PoCo::RSSAggregator
# <Hixie> mozbot: shutup please
# <mozbot> Ok, threw away 2558 messages.
# Ahem. So now we limit the diff reporting code to maxInChannel
# items at a time...
if (@output and @output < $self->{'maxInChannel'}) {
@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, $_);
}
}
}